Compare commits
134 Commits
Author | SHA1 | Date | |
---|---|---|---|
![]() |
9826879bcd | ||
a7103f8510 | |||
![]() |
d931a68b3e | ||
![]() |
a297adab7a | ||
![]() |
b5fd438ce0 | ||
![]() |
a4a5bf0d37 | ||
180ac51aea | |||
73b19ca1c4 | |||
31e8802803 | |||
fe860b2e38 | |||
60b575c8f8 | |||
3a20cb717c | |||
3bbfe0f0ec | |||
04d984d4fc | |||
05c4ed4963 | |||
7cf69d055a | |||
4c80cb3ed1 | |||
4c5e3ae151 | |||
86d0d11869 | |||
bf8c609dbd | |||
706aa4a787 | |||
dd5b85d2c8 | |||
53aa3b3679 | |||
6c33a2f4c0 | |||
ea77f3d72e | |||
c190d27467 | |||
ecf4c5da8c | |||
6285a47f72 | |||
5d4fa15eb9 | |||
bb4e4d7e56 | |||
c9bbd542c5 | |||
6e99d91dae | |||
4c2071e906 | |||
b49671d530 | |||
5c11c469f2 | |||
361290e24f | |||
65d1a5ecec | |||
6af367ba76 | |||
65907a96f4 | |||
05c1724234 | |||
4180300e87 | |||
c61f5a8772 | |||
fdcff58318 | |||
72e60e97ea | |||
132b2fd536 | |||
12835ad23a | |||
06a2262db9 | |||
cc5578e306 | |||
8765588667 | |||
9a8dc382a2 | |||
85859a488d | |||
e54e3e684e | |||
17c74d2e32 | |||
c60f22f7f7 | |||
36cf3121d1 | |||
c8b95313ff | |||
e5448af6d8 | |||
49149e452c | |||
b2d91391b3 | |||
13f70be87b | |||
71aa33d22f | |||
4c3da12ea1 | |||
a47082730a | |||
99dc3904af | |||
acf18c1bb4 | |||
e9788e22ad | |||
6cb983a7c4 | |||
7496a6a866 | |||
c8750b9dfd | |||
d98ca97624 | |||
61d57901b1 | |||
9c82038561 | |||
15dfa48d2d | |||
2457e50712 | |||
92c36502bc | |||
439b716a71 | |||
97282ff051 | |||
093c91521a | |||
74aa7b4ea6 | |||
3d8c84e17c | |||
91e8367d35 | |||
129020e1c5 | |||
d03fe10be8 | |||
3fdbb95080 | |||
![]() |
3103018404 | ||
![]() |
478ed1f5d0 | ||
42feb53c86 | |||
391d695144 | |||
038b9d8564 | |||
5448b5c0ec | |||
be45235d15 | |||
87e4b829c5 | |||
1ab605b70d | |||
fbb78ae22d | |||
a21dc76d81 | |||
49cecb798a | |||
e31f0f1ded | |||
43792fb349 | |||
d6a83a0520 | |||
c803a7566f | |||
670edb6ddc | |||
f5d1e0a3cb | |||
a58b3d194c | |||
d9180d950b | |||
9da7234684 | |||
610b1223d9 | |||
6954f05dee | |||
db57136f1b | |||
2c111fd98c | |||
90f8ae4188 | |||
b4d445b378 | |||
ea79c90c89 | |||
1b2f72cc3a | |||
505245d54c | |||
93ab5a5833 | |||
247ea7e7c3 | |||
d1e8a62396 | |||
2ac67cff18 | |||
eed2273ddf | |||
31d6eb076c | |||
bb5e406af3 | |||
aab33ccfab | |||
98164afc90 | |||
819e83d138 | |||
16b9b2ba85 | |||
9d94189165 | |||
![]() |
9fb53df45d | ||
92cc779caf | |||
e3d72fb14e | |||
014152fc89 | |||
3a300c816e | |||
8c8bde4a0f | |||
0c64f62d01 | |||
d5d5f2fcd4 |
45
.gitea/workflows/ci.yaml
Normal file
45
.gitea/workflows/ci.yaml
Normal file
@ -0,0 +1,45 @@
|
||||
name: CI
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- master
|
||||
pull_request:
|
||||
branches:
|
||||
- master
|
||||
|
||||
jobs:
|
||||
build:local:
|
||||
runs-on: native
|
||||
steps:
|
||||
- uses: https://gitea.com/ScMi1/checkout@v1.4
|
||||
- run: nix build -L --no-link .#ovniPackages.local
|
||||
|
||||
build:rt:
|
||||
runs-on: native
|
||||
steps:
|
||||
- uses: https://gitea.com/ScMi1/checkout@v1.4
|
||||
- run: nix build -L --no-link .#ovniPackages.rt
|
||||
|
||||
build:debug:
|
||||
runs-on: native
|
||||
steps:
|
||||
- uses: https://gitea.com/ScMi1/checkout@v1.4
|
||||
- run: nix build -L --no-link .#ovniPackages.debug
|
||||
|
||||
build:asan:
|
||||
runs-on: native
|
||||
steps:
|
||||
- uses: https://gitea.com/ScMi1/checkout@v1.4
|
||||
- run: nix build -L --no-link .#ovniPackages.asan
|
||||
|
||||
build:nompi:
|
||||
runs-on: native
|
||||
steps:
|
||||
- uses: https://gitea.com/ScMi1/checkout@v1.4
|
||||
- run: nix build -L --no-link .#ovniPackages.nompi
|
||||
|
||||
build:compilers:
|
||||
runs-on: native
|
||||
steps:
|
||||
- uses: https://gitea.com/ScMi1/checkout@v1.4
|
||||
- run: nix build -L --no-link .#ovniPackages.compilers
|
@ -42,6 +42,13 @@ build:asan:
|
||||
script:
|
||||
- nix build -L --no-link .#ovniPackages.asan
|
||||
|
||||
build:ubsan:
|
||||
stage: build
|
||||
tags:
|
||||
- nix
|
||||
script:
|
||||
- nix build -L --no-link .#ovniPackages.ubsan
|
||||
|
||||
build:nompi:
|
||||
stage: build
|
||||
tags:
|
||||
@ -55,3 +62,24 @@ build:compilers:
|
||||
- nix
|
||||
script:
|
||||
- nix build -L --no-link .#ovniPackages.compilers
|
||||
|
||||
build:armv7:
|
||||
stage: build
|
||||
tags:
|
||||
- nix
|
||||
script:
|
||||
- nix build -L --no-link .#ovniPackages.armv7
|
||||
|
||||
build:aarch64:
|
||||
stage: build
|
||||
tags:
|
||||
- nix
|
||||
script:
|
||||
- nix build -L --no-link .#ovniPackages.aarch64
|
||||
|
||||
build:riscv64:
|
||||
stage: build
|
||||
tags:
|
||||
- nix
|
||||
script:
|
||||
- nix build -L --no-link .#ovniPackages.riscv64
|
||||
|
58
CHANGELOG.md
58
CHANGELOG.md
@ -7,6 +7,61 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
||||
|
||||
## [Unreleased]
|
||||
|
||||
### Changed
|
||||
|
||||
- OpenMP model version increased to 1.2.0.
|
||||
|
||||
### Added
|
||||
|
||||
- Add support OpenMP label and task ID views.
|
||||
|
||||
## [1.11.0] - 2024-11-08
|
||||
|
||||
### Added
|
||||
|
||||
- Introduce part model.
|
||||
- Support for `nosv_cond_wait`, `nosv_cond_signal` and `nosv_cond_broadcast` events VA{oOgGkK}.
|
||||
|
||||
### Changed
|
||||
|
||||
- Enable -Wconversion and -Wsign-conversion.
|
||||
- Update trace format to version 3.
|
||||
- The ovni.require metadata key is now mandatory.
|
||||
- Store process metadata in thread metadata.
|
||||
- nOS-V model version increased to 2.4.0.
|
||||
|
||||
### Fixed
|
||||
|
||||
- Fix bug in ovnisort when injecting events in a previously modified section.
|
||||
|
||||
## [1.10.0] - 2024-07-26
|
||||
|
||||
### Changed
|
||||
|
||||
- nOS-V model version increased to 2.3.0.
|
||||
- Prevent accidental use of nOS-V traces without required events for the
|
||||
breakdown model using the `nosv.can_breakdown` attribute.
|
||||
- Increase ovni model version to 1.1.0 for the mark events `OM*`.
|
||||
|
||||
### Added
|
||||
|
||||
- Add support for nOS-V progressing events VP{pra}.
|
||||
- Add breakdown model for nOS-V.
|
||||
- New API to manage stream metadata `ovni_attr_*()`.
|
||||
- New mark API `ovni_mark_*()` to emit user-defined events.
|
||||
|
||||
### Fixed
|
||||
|
||||
- Cross-compilation for ARM 32 bits.
|
||||
|
||||
## [1.9.1] - 2024-05-10
|
||||
|
||||
### Fixed
|
||||
|
||||
- Fix breakdown model error that was preventing a zero value to be written in
|
||||
the PRV trace.
|
||||
- Fix gcc 11.3.1 -Wstringop-overflow warning.
|
||||
|
||||
## [1.9.0] - 2024-04-25
|
||||
|
||||
### Added
|
||||
@ -238,6 +293,9 @@ are used along with some other changes.
|
||||
- First ovni release.
|
||||
|
||||
[unreleased]: https://jungle.bsc.es/git/rarias/ovni
|
||||
[1.11.0]: https://github.com/rodarima/ovni/releases/tag/1.11.0
|
||||
[1.10.0]: https://github.com/rodarima/ovni/releases/tag/1.10.0
|
||||
[1.9.1]: https://github.com/rodarima/ovni/releases/tag/1.9.1
|
||||
[1.9.0]: https://github.com/rodarima/ovni/releases/tag/1.9.0
|
||||
[1.8.0]: https://github.com/rodarima/ovni/releases/tag/1.8.0
|
||||
[1.7.0]: https://github.com/rodarima/ovni/releases/tag/1.7.0
|
||||
|
@ -3,13 +3,13 @@
|
||||
|
||||
cmake_minimum_required(VERSION 3.20)
|
||||
|
||||
project(OVNI LANGUAGES C VERSION 1.9.0)
|
||||
project(OVNI LANGUAGES C VERSION 1.11.0)
|
||||
|
||||
list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake/")
|
||||
|
||||
add_compile_options(-Wall -Wextra -Wformat
|
||||
-Wmissing-prototypes -Wstrict-prototypes
|
||||
#-Wconversion -Wsign-conversion
|
||||
-Wconversion -Wsign-conversion
|
||||
-Wold-style-definition -pedantic
|
||||
-Werror
|
||||
)
|
||||
@ -67,7 +67,9 @@ set(CMAKE_C_FLAGS_ASAN "${CMAKE_C_FLAGS_DEBUG} \
|
||||
-fno-omit-frame-pointer"
|
||||
CACHE STRING "Flags used by the C compiler during AddressSanitizer builds." FORCE)
|
||||
|
||||
set(CMAKE_C_FLAGS_UBSAN "${CMAKE_C_FLAGS_DEBUG} -fsanitize=undefined"
|
||||
set(CMAKE_C_FLAGS_UBSAN "${CMAKE_C_FLAGS_DEBUG} \
|
||||
-fsanitize=undefined \
|
||||
-fno-sanitize-recover=all"
|
||||
CACHE STRING "Flags used by the C compiler during UndefinedBehaviorSanitizer builds." FORCE)
|
||||
|
||||
find_program(IWYU NAMES include-what-you-use iwyu)
|
||||
|
44
cfg/cpu/nosv/breakdown.cfg
Normal file
44
cfg/cpu/nosv/breakdown.cfg
Normal file
@ -0,0 +1,44 @@
|
||||
#ParaverCFG
|
||||
ConfigFile.Version: 3.4
|
||||
ConfigFile.NumWindows: 1
|
||||
|
||||
|
||||
################################################################################
|
||||
< NEW DISPLAYING WINDOW CPU: nOS-V Runtime/Idle/Task breakdown >
|
||||
################################################################################
|
||||
window_name CPU: nOS-V Runtime/Idle/Task breakdown
|
||||
window_type single
|
||||
window_id 1
|
||||
window_position_x 0
|
||||
window_position_y 0
|
||||
window_width 600
|
||||
window_height 150
|
||||
window_comm_lines_enabled false
|
||||
window_flags_enabled false
|
||||
window_noncolor_mode true
|
||||
window_custom_color_enabled true
|
||||
window_custom_color_palette {6.000000000000:94,0,0},{7.000000000000:153,114,0},{9.000000000000:124,213,228},{10.000000000000:242,239,141},{11.000000000000:0,70,0},{19.000000000000:195,96,151},{20.000000000000:255,162,255},{21.000000000000:203,255,3},{22.000000000000:7,255,12},{23.000000000000:21,224,189},{24.000000000000:255,103,0},{25.000000000000:0,99,162},{26.000000000000:110,77,252},{100.000000000000:0,100,0},{101.000000000000:60,60,60},{102.000000000000:150,150,0}
|
||||
window_logical_filtered true
|
||||
window_physical_filtered false
|
||||
window_comm_fromto true
|
||||
window_comm_tagsize true
|
||||
window_comm_typeval true
|
||||
window_units Microseconds
|
||||
window_maximum_y 1000.0
|
||||
window_minimum_y 1.0
|
||||
window_compute_y_max true
|
||||
window_level thread
|
||||
window_scale_relative 1.000000000000
|
||||
window_end_time_relative 1.000000000000
|
||||
window_object appl { 1, { All } }
|
||||
window_begin_time_relative 0.000000000000
|
||||
window_open true
|
||||
window_drawmode draw_randnotzero
|
||||
window_drawmode_rows draw_randnotzero
|
||||
window_pixel_size 1
|
||||
window_labels_to_draw 1
|
||||
window_selected_functions { 14, { {cpu, Active Thd}, {appl, Adding}, {task, Adding}, {thread, Last Evt Val}, {node, Adding}, {system, Adding}, {workload, Adding}, {from_obj, All}, {to_obj, All}, {tag_msg, All}, {size_msg, All}, {bw_msg, All}, {evt_type, =}, {evt_value, All} } }
|
||||
window_compose_functions { 9, { {compose_cpu, As Is}, {compose_appl, As Is}, {compose_task, As Is}, {compose_thread, As Is}, {compose_node, As Is}, {compose_system, As Is}, {compose_workload, As Is}, {topcompose1, As Is}, {topcompose2, As Is} } }
|
||||
window_filter_module evt_type 1 17
|
||||
window_filter_module evt_type_label 1 "CPU: nOS-V Runtime/Idle/Task breakdown"
|
||||
|
44
cfg/cpu/nosv/idle.cfg
Normal file
44
cfg/cpu/nosv/idle.cfg
Normal file
@ -0,0 +1,44 @@
|
||||
#ParaverCFG
|
||||
ConfigFile.Version: 3.4
|
||||
ConfigFile.NumWindows: 1
|
||||
|
||||
|
||||
################################################################################
|
||||
< NEW DISPLAYING WINDOW CPU: nOS-V idle state of the RUNNING thread >
|
||||
################################################################################
|
||||
window_name CPU: nOS-V idle state of the RUNNING thread
|
||||
window_type single
|
||||
window_id 1
|
||||
window_position_x 0
|
||||
window_position_y 0
|
||||
window_width 600
|
||||
window_height 150
|
||||
window_comm_lines_enabled false
|
||||
window_flags_enabled false
|
||||
window_noncolor_mode true
|
||||
window_custom_color_enabled true
|
||||
window_custom_color_palette {100.000000000000:0,100,0},{101.000000000000:60,60,60},{102.000000000000:150,150,0}
|
||||
window_logical_filtered true
|
||||
window_physical_filtered false
|
||||
window_comm_fromto true
|
||||
window_comm_tagsize true
|
||||
window_comm_typeval true
|
||||
window_units Microseconds
|
||||
window_maximum_y 1000.0
|
||||
window_minimum_y 1.0
|
||||
window_compute_y_max true
|
||||
window_level thread
|
||||
window_scale_relative 1.000000000000
|
||||
window_end_time_relative 1.000000000000
|
||||
window_object appl { 1, { All } }
|
||||
window_begin_time_relative 0.000000000000
|
||||
window_open true
|
||||
window_drawmode draw_randnotzero
|
||||
window_drawmode_rows draw_randnotzero
|
||||
window_pixel_size 1
|
||||
window_labels_to_draw 1
|
||||
window_selected_functions { 14, { {cpu, Active Thd}, {appl, Adding}, {task, Adding}, {thread, Last Evt Val}, {node, Adding}, {system, Adding}, {workload, Adding}, {from_obj, All}, {to_obj, All}, {tag_msg, All}, {size_msg, All}, {bw_msg, All}, {evt_type, =}, {evt_value, All} } }
|
||||
window_compose_functions { 9, { {compose_cpu, As Is}, {compose_appl, As Is}, {compose_task, As Is}, {compose_thread, As Is}, {compose_node, As Is}, {compose_system, As Is}, {compose_workload, As Is}, {topcompose1, As Is}, {topcompose2, As Is} } }
|
||||
window_filter_module evt_type 1 16
|
||||
window_filter_module evt_type_label 1 "CPU: nOS-V idle state of the RUNNING thread"
|
||||
|
@ -17,7 +17,7 @@ window_comm_lines_enabled true
|
||||
window_flags_enabled false
|
||||
window_noncolor_mode true
|
||||
window_custom_color_enabled true
|
||||
window_custom_color_palette {6.000000000000:94,0,0},{7.000000000000:153,114,0},{9.000000000000:124,213,228},{10.000000000000:242,239,141},{11.000000000000:0,70,0},{19.000000000000:195,96,151},{20.000000000000:255,162,255},{21.000000000000:203,255,3},{22.000000000000:7,255,12},{23.000000000000:21,224,189},{24.000000000000:255,103,0},{25.000000000000:0,99,162},{26.000000000000:110,77,252}
|
||||
window_custom_color_palette {6.000000000000:94,0,0},{7.000000000000:153,114,0},{9.000000000000:124,213,228},{10.000000000000:242,239,141},{11.000000000000:0,70,0},{19.000000000000:195,96,151},{20.000000000000:255,162,255},{21.000000000000:203,255,3},{22.000000000000:7,255,12},{23.000000000000:21,165,118},{24.000000000000:255,103,0},{25.000000000000:200,30,5},{26.000000000000:255,10,200},{27.000000000000:98,133,80},{28.000000000000:0,99,162},{29.000000000000:110,77,252}
|
||||
window_logical_filtered true
|
||||
window_physical_filtered false
|
||||
window_comm_fromto true
|
||||
|
41
cfg/cpu/openmp/label.cfg
Normal file
41
cfg/cpu/openmp/label.cfg
Normal file
@ -0,0 +1,41 @@
|
||||
#ParaverCFG
|
||||
ConfigFile.Version: 3.4
|
||||
ConfigFile.NumWindows: 1
|
||||
|
||||
|
||||
################################################################################
|
||||
< NEW DISPLAYING WINDOW CPU: OpenMP label of the RUNNING thread >
|
||||
################################################################################
|
||||
window_name CPU: OpenMP label of the RUNNING thread
|
||||
window_type single
|
||||
window_id 1
|
||||
window_position_x 100
|
||||
window_position_y 100
|
||||
window_width 600
|
||||
window_height 150
|
||||
window_comm_lines_enabled true
|
||||
window_flags_enabled false
|
||||
window_noncolor_mode true
|
||||
window_logical_filtered true
|
||||
window_physical_filtered false
|
||||
window_comm_fromto true
|
||||
window_comm_tagsize true
|
||||
window_comm_typeval true
|
||||
window_units Microseconds
|
||||
window_maximum_y 1000.0
|
||||
window_minimum_y 1.0
|
||||
window_compute_y_max true
|
||||
window_level thread
|
||||
window_scale_relative 1.000000000000
|
||||
window_end_time_relative 1.000000000000
|
||||
window_object appl { 1, { All } }
|
||||
window_begin_time_relative 0.000000000000
|
||||
window_open true
|
||||
window_drawmode draw_randnotzero
|
||||
window_drawmode_rows draw_randnotzero
|
||||
window_pixel_size 1
|
||||
window_labels_to_draw 1
|
||||
window_selected_functions { 14, { {cpu, Active Thd}, {appl, Adding}, {task, Adding}, {thread, Last Evt Val}, {node, Adding}, {system, Adding}, {workload, Adding}, {from_obj, All}, {to_obj, All}, {tag_msg, All}, {size_msg, All}, {bw_msg, All}, {evt_type, =}, {evt_value, All} } }
|
||||
window_compose_functions { 9, { {compose_cpu, As Is}, {compose_appl, As Is}, {compose_task, As Is}, {compose_thread, As Is}, {compose_node, As Is}, {compose_system, As Is}, {compose_workload, As Is}, {topcompose1, As Is}, {topcompose2, As Is} } }
|
||||
window_filter_module evt_type 1 51
|
||||
window_filter_module evt_type_label 1 "CPU: OpenMP label of the RUNNING thread"
|
41
cfg/cpu/openmp/task-id.cfg
Normal file
41
cfg/cpu/openmp/task-id.cfg
Normal file
@ -0,0 +1,41 @@
|
||||
#ParaverCFG
|
||||
ConfigFile.Version: 3.4
|
||||
ConfigFile.NumWindows: 1
|
||||
|
||||
|
||||
################################################################################
|
||||
< NEW DISPLAYING WINDOW CPU: OpenMP task id of the RUNNING thread >
|
||||
################################################################################
|
||||
window_name CPU: OpenMP task id of the RUNNING thread
|
||||
window_type single
|
||||
window_id 1
|
||||
window_position_x 100
|
||||
window_position_y 100
|
||||
window_width 600
|
||||
window_height 150
|
||||
window_comm_lines_enabled true
|
||||
window_flags_enabled false
|
||||
window_noncolor_mode true
|
||||
window_logical_filtered true
|
||||
window_physical_filtered false
|
||||
window_comm_fromto true
|
||||
window_comm_tagsize true
|
||||
window_comm_typeval true
|
||||
window_units Microseconds
|
||||
window_maximum_y 1000.0
|
||||
window_minimum_y 1.0
|
||||
window_compute_y_max true
|
||||
window_level thread
|
||||
window_scale_relative 1.000000000000
|
||||
window_end_time_relative 1.000000000000
|
||||
window_object appl { 1, { All } }
|
||||
window_begin_time_relative 0.000000000000
|
||||
window_open true
|
||||
window_drawmode draw_randnotzero
|
||||
window_drawmode_rows draw_randnotzero
|
||||
window_pixel_size 1
|
||||
window_labels_to_draw 1
|
||||
window_selected_functions { 14, { {cpu, Active Thd}, {appl, Adding}, {task, Adding}, {thread, Last Evt Val}, {node, Adding}, {system, Adding}, {workload, Adding}, {from_obj, All}, {to_obj, All}, {tag_msg, All}, {size_msg, All}, {bw_msg, All}, {evt_type, =}, {evt_value, All} } }
|
||||
window_compose_functions { 9, { {compose_cpu, As Is}, {compose_appl, As Is}, {compose_task, As Is}, {compose_thread, As Is}, {compose_node, As Is}, {compose_system, As Is}, {compose_workload, As Is}, {topcompose1, As Is}, {topcompose2, As Is} } }
|
||||
window_filter_module evt_type 1 52
|
||||
window_filter_module evt_type_label 1 "CPU: OpenMP task id of the RUNNING thread"
|
41
cfg/cpu/ovni/mark.cfg
Normal file
41
cfg/cpu/ovni/mark.cfg
Normal file
@ -0,0 +1,41 @@
|
||||
#ParaverCFG
|
||||
ConfigFile.Version: 3.4
|
||||
ConfigFile.NumWindows: 1
|
||||
|
||||
|
||||
################################################################################
|
||||
< NEW DISPLAYING WINDOW CPU: Mark value of the RUNNING thread >
|
||||
################################################################################
|
||||
window_name CPU: Mark value of the RUNNING thread
|
||||
window_type single
|
||||
window_id 1
|
||||
window_position_x 100
|
||||
window_position_y 100
|
||||
window_width 600
|
||||
window_height 150
|
||||
window_comm_lines_enabled true
|
||||
window_flags_enabled false
|
||||
window_noncolor_mode true
|
||||
window_logical_filtered true
|
||||
window_physical_filtered false
|
||||
window_comm_fromto true
|
||||
window_comm_tagsize true
|
||||
window_comm_typeval true
|
||||
window_units Microseconds
|
||||
window_maximum_y 1000.0
|
||||
window_minimum_y 1.0
|
||||
window_compute_y_max true
|
||||
window_level thread
|
||||
window_scale_relative 1.000000000000
|
||||
window_end_time_relative 1.000000000000
|
||||
window_object appl { 1, { All } }
|
||||
window_begin_time_relative 0.000000000000
|
||||
window_open true
|
||||
window_drawmode draw_randnotzero
|
||||
window_drawmode_rows draw_randnotzero
|
||||
window_pixel_size 1
|
||||
window_labels_to_draw 1
|
||||
window_selected_functions { 14, { {cpu, Active Thd}, {appl, Adding}, {task, Adding}, {thread, Last Evt Val}, {node, Adding}, {system, Adding}, {workload, Adding}, {from_obj, All}, {to_obj, All}, {tag_msg, All}, {size_msg, All}, {bw_msg, All}, {evt_type, =}, {evt_value, All} } }
|
||||
window_compose_functions { 9, { {compose_cpu, As Is}, {compose_appl, As Is}, {compose_task, As Is}, {compose_thread, As Is}, {compose_node, As Is}, {compose_system, As Is}, {compose_workload, As Is}, {topcompose1, As Is}, {topcompose2, As Is} } }
|
||||
window_filter_module evt_type 1 100
|
||||
window_filter_module evt_type_label 1 "CPU: Mark value of the RUNNING thread"
|
@ -17,7 +17,7 @@ window_comm_lines_enabled true
|
||||
window_flags_enabled false
|
||||
window_noncolor_mode true
|
||||
window_custom_color_enabled true
|
||||
window_custom_color_palette {6.000000000000:94,0,0},{7.000000000000:153,114,0},{9.000000000000:124,213,228},{10.000000000000:242,239,141},{11.000000000000:0,70,0},{19.000000000000:195,96,151},{20.000000000000:255,162,255},{21.000000000000:203,255,3},{22.000000000000:7,255,12},{23.000000000000:21,224,189},{24.000000000000:255,103,0},{25.000000000000:0,99,162},{26.000000000000:110,77,252}
|
||||
window_custom_color_palette {6.000000000000:94,0,0},{7.000000000000:153,114,0},{9.000000000000:124,213,228},{10.000000000000:242,239,141},{11.000000000000:0,70,0},{19.000000000000:195,96,151},{20.000000000000:255,162,255},{21.000000000000:203,255,3},{22.000000000000:7,255,12},{23.000000000000:21,165,118},{24.000000000000:255,103,0},{25.000000000000:200,30,5},{26.000000000000:255,10,200},{27.000000000000:98,133,80},{28.000000000000:0,99,162},{29.000000000000:110,77,252}
|
||||
window_logical_filtered true
|
||||
window_physical_filtered false
|
||||
window_comm_fromto true
|
||||
|
41
cfg/thread/openmp/label.cfg
Normal file
41
cfg/thread/openmp/label.cfg
Normal file
@ -0,0 +1,41 @@
|
||||
#ParaverCFG
|
||||
ConfigFile.Version: 3.4
|
||||
ConfigFile.NumWindows: 1
|
||||
|
||||
|
||||
################################################################################
|
||||
< NEW DISPLAYING WINDOW Thread: OpenMP label of the ACTIVE thread >
|
||||
################################################################################
|
||||
window_name Thread: OpenMP label of the ACTIVE thread
|
||||
window_type single
|
||||
window_id 1
|
||||
window_position_x 100
|
||||
window_position_y 100
|
||||
window_width 600
|
||||
window_height 150
|
||||
window_comm_lines_enabled true
|
||||
window_flags_enabled false
|
||||
window_noncolor_mode true
|
||||
window_logical_filtered true
|
||||
window_physical_filtered false
|
||||
window_comm_fromto true
|
||||
window_comm_tagsize true
|
||||
window_comm_typeval true
|
||||
window_units Microseconds
|
||||
window_maximum_y 1000.0
|
||||
window_minimum_y 1.0
|
||||
window_compute_y_max true
|
||||
window_level thread
|
||||
window_scale_relative 1.000000000000
|
||||
window_end_time_relative 1.000000000000
|
||||
window_object appl { 1, { All } }
|
||||
window_begin_time_relative 0.000000000000
|
||||
window_open true
|
||||
window_drawmode draw_randnotzero
|
||||
window_drawmode_rows draw_randnotzero
|
||||
window_pixel_size 1
|
||||
window_labels_to_draw 1
|
||||
window_selected_functions { 14, { {cpu, Active Thd}, {appl, Adding}, {task, Adding}, {thread, Last Evt Val}, {node, Adding}, {system, Adding}, {workload, Adding}, {from_obj, All}, {to_obj, All}, {tag_msg, All}, {size_msg, All}, {bw_msg, All}, {evt_type, =}, {evt_value, All} } }
|
||||
window_compose_functions { 9, { {compose_cpu, As Is}, {compose_appl, As Is}, {compose_task, As Is}, {compose_thread, As Is}, {compose_node, As Is}, {compose_system, As Is}, {compose_workload, As Is}, {topcompose1, As Is}, {topcompose2, As Is} } }
|
||||
window_filter_module evt_type 1 51
|
||||
window_filter_module evt_type_label 1 "Thread: OpenMP label of the ACTIVE thread"
|
41
cfg/thread/openmp/task-id.cfg
Normal file
41
cfg/thread/openmp/task-id.cfg
Normal file
@ -0,0 +1,41 @@
|
||||
#ParaverCFG
|
||||
ConfigFile.Version: 3.4
|
||||
ConfigFile.NumWindows: 1
|
||||
|
||||
|
||||
################################################################################
|
||||
< NEW DISPLAYING WINDOW Thread: OpenMP task id of the ACTIVE thread >
|
||||
################################################################################
|
||||
window_name Thread: OpenMP task id of the ACTIVE thread
|
||||
window_type single
|
||||
window_id 1
|
||||
window_position_x 100
|
||||
window_position_y 100
|
||||
window_width 600
|
||||
window_height 150
|
||||
window_comm_lines_enabled true
|
||||
window_flags_enabled false
|
||||
window_noncolor_mode true
|
||||
window_logical_filtered true
|
||||
window_physical_filtered false
|
||||
window_comm_fromto true
|
||||
window_comm_tagsize true
|
||||
window_comm_typeval true
|
||||
window_units Microseconds
|
||||
window_maximum_y 1000.0
|
||||
window_minimum_y 1.0
|
||||
window_compute_y_max true
|
||||
window_level thread
|
||||
window_scale_relative 1.000000000000
|
||||
window_end_time_relative 1.000000000000
|
||||
window_object appl { 1, { All } }
|
||||
window_begin_time_relative 0.000000000000
|
||||
window_open true
|
||||
window_drawmode draw_randnotzero
|
||||
window_drawmode_rows draw_randnotzero
|
||||
window_pixel_size 1
|
||||
window_labels_to_draw 1
|
||||
window_selected_functions { 14, { {cpu, Active Thd}, {appl, Adding}, {task, Adding}, {thread, Last Evt Val}, {node, Adding}, {system, Adding}, {workload, Adding}, {from_obj, All}, {to_obj, All}, {tag_msg, All}, {size_msg, All}, {bw_msg, All}, {evt_type, =}, {evt_value, All} } }
|
||||
window_compose_functions { 9, { {compose_cpu, As Is}, {compose_appl, As Is}, {compose_task, As Is}, {compose_thread, As Is}, {compose_node, As Is}, {compose_system, As Is}, {compose_workload, As Is}, {topcompose1, As Is}, {topcompose2, As Is} } }
|
||||
window_filter_module evt_type 1 52
|
||||
window_filter_module evt_type_label 1 "Thread: OpenMP task id of the ACTIVE thread"
|
41
cfg/thread/ovni/mark.cfg
Normal file
41
cfg/thread/ovni/mark.cfg
Normal file
@ -0,0 +1,41 @@
|
||||
#ParaverCFG
|
||||
ConfigFile.Version: 3.4
|
||||
ConfigFile.NumWindows: 1
|
||||
|
||||
|
||||
################################################################################
|
||||
< NEW DISPLAYING WINDOW Thread: Mark value of the ACTIVE thread >
|
||||
################################################################################
|
||||
window_name Thread: Mark value of the ACTIVE thread
|
||||
window_type single
|
||||
window_id 1
|
||||
window_position_x 100
|
||||
window_position_y 100
|
||||
window_width 600
|
||||
window_height 150
|
||||
window_comm_lines_enabled true
|
||||
window_flags_enabled false
|
||||
window_noncolor_mode true
|
||||
window_logical_filtered true
|
||||
window_physical_filtered false
|
||||
window_comm_fromto true
|
||||
window_comm_tagsize true
|
||||
window_comm_typeval true
|
||||
window_units Microseconds
|
||||
window_maximum_y 1000.0
|
||||
window_minimum_y 1.0
|
||||
window_compute_y_max true
|
||||
window_level thread
|
||||
window_scale_relative 1.000000000000
|
||||
window_end_time_relative 1.000000000000
|
||||
window_object appl { 1, { All } }
|
||||
window_begin_time_relative 0.000000000000
|
||||
window_open true
|
||||
window_drawmode draw_randnotzero
|
||||
window_drawmode_rows draw_randnotzero
|
||||
window_pixel_size 1
|
||||
window_labels_to_draw 1
|
||||
window_selected_functions { 14, { {cpu, Active Thd}, {appl, Adding}, {task, Adding}, {thread, Last Evt Val}, {node, Adding}, {system, Adding}, {workload, Adding}, {from_obj, All}, {to_obj, All}, {tag_msg, All}, {size_msg, All}, {bw_msg, All}, {evt_type, =}, {evt_value, All} } }
|
||||
window_compose_functions { 9, { {compose_cpu, As Is}, {compose_appl, As Is}, {compose_task, As Is}, {compose_thread, As Is}, {compose_node, As Is}, {compose_system, As Is}, {compose_workload, As Is}, {topcompose1, As Is}, {topcompose2, As Is} } }
|
||||
window_filter_module evt_type 1 100
|
||||
window_filter_module evt_type_label 1 "Thread: Mark value of the ACTIVE thread"
|
@ -65,5 +65,5 @@ to write the duplicated value with no error.
|
||||
A unique function can be set to each channel which will be called once a channel
|
||||
becomes dirty with `chan_set_dirty_cb()`. This callback will be called before
|
||||
`chan_set()`, `chan_push()` or `chan_pop()` returns. The [patch
|
||||
bay](../patchbay) uses this callback to detect when a channel is modified an run
|
||||
bay](patchbay.md) uses this callback to detect when a channel is modified an run
|
||||
other callbacks.
|
||||
|
@ -22,7 +22,7 @@ If the model is not enabled, no other function will be called.
|
||||
|
||||
The create function is called for each enabled model to allow them to allocate
|
||||
all the required structures to perform the emulation using the
|
||||
[extend](../extend) mechanism. All the required channels must be created and
|
||||
[extend](extend.md) mechanism. All the required channels must be created and
|
||||
registered in the patch bay in this function, so other models can found them in
|
||||
the next stage.
|
||||
|
||||
|
@ -1,6 +1,6 @@
|
||||
# Mux
|
||||
|
||||
The emulator provides a mechanism to interconnect [channels](../channels) in a
|
||||
The emulator provides a mechanism to interconnect [channels](channels.md) in a
|
||||
similar way as an [analog
|
||||
multiplexer](https://en.wikipedia.org/wiki/Multiplexer) by using the `mux`
|
||||
module.
|
||||
@ -19,7 +19,7 @@ selected. This allows a multiplexer to act as a filter too.
|
||||
|
||||
The typical use of multiplexers is to implement the tracking modes of channels.
|
||||
As an example, the following diagram shows two multiplexers used to implement
|
||||
the subsystem view of [Nanos6](../nanos6):
|
||||
the subsystem view of [Nanos6](../user/emulation/nanos6.md):
|
||||
|
||||

|
||||
|
||||
@ -51,5 +51,5 @@ Multiplexers allow models to interact with each other in a controlled way. In
|
||||
the example, the blue channel (*nanos6.thread0.subsystem*) is directly modified by
|
||||
the Nanos6 model when a new event is received. While the red channels are
|
||||
controlled by the ovni model. The rest of the channels are automatically updated
|
||||
in the propagation phase of the [bay](../patchbay) allowing the ovni model to
|
||||
in the propagation phase of the [bay](patchbay.md) allowing the ovni model to
|
||||
modify the Nanos6 Paraver view of the subsystems.
|
||||
|
@ -16,7 +16,7 @@ A channel can be connected to each row in a trace with `prv_register()`, so the
|
||||
new values of the channel get written in the trace. Only null and int64 data
|
||||
values are supported for now.
|
||||
|
||||
The emission phase is controlled by the [patch bay](../patchbay) and runs all
|
||||
The emission phase is controlled by the [patch bay](patchbay.md) and runs all
|
||||
the emit callbacks at once for all dirty channels.
|
||||
|
||||
## Duplicate values
|
||||
|
@ -1,6 +1,6 @@
|
||||
# Patch bay
|
||||
|
||||
The patch bay (or simply bay) allows [channels](../channels/) to be registered
|
||||
The patch bay (or simply bay) allows [channels](channels.md) to be registered
|
||||
with their name so they are visible to all parts of the emulator and provides a
|
||||
way to run callbacks when the channels update their values.
|
||||
|
||||
|
@ -13,8 +13,9 @@ The ovni project implements a fast instrumentation library that records
|
||||
small events (starting at 12 bytes) during the execution of programs to
|
||||
later investigate how the execution happened.
|
||||
|
||||
The instrumentation process is split in two stages: [runtime](runtime)
|
||||
tracing and [emulation](emulation/).
|
||||
The instrumentation process is split in two stages:
|
||||
[runtime](user/runtime/index.md)
|
||||
tracing and [emulation](user/emulation/index.md).
|
||||
|
||||
During runtime, very short binary events are stored on disk which
|
||||
describe what is happening. Once the execution finishes, the events are
|
||||
|
@ -1,4 +1,4 @@
|
||||
mkdocs==1.4.1
|
||||
mkdocs==1.6.0
|
||||
markdown==3.3.7
|
||||
python-markdown-math==0.8
|
||||
jinja2==3.1.2
|
||||
|
@ -1,31 +0,0 @@
|
||||
# Overview
|
||||
|
||||
The objective of the ovni project is to provide insight into what
|
||||
happened at execution of a program.
|
||||
|
||||

|
||||
|
||||
The key pieces of software involved are instrumented so they emit events
|
||||
during the execution which allow the reconstruction of the execution
|
||||
later on.
|
||||
|
||||
During the execution phase, the information gathered in the events is
|
||||
kept very short and simple, so the overhead is kept at minimum to avoid
|
||||
disturbing the execution process. Here is an example of a single event
|
||||
emitted during the execution phase, informing the current thread to
|
||||
finish the execution:
|
||||
|
||||
00 4f 48 65 52 c0 27 b4 d3 ec 01 00
|
||||
|
||||
During the emulation phase, the events are read and processed in the
|
||||
emulator, reconstructing the execution. State transitions are recorded
|
||||
in a Paraver trace. Here is an example of the same thread ceasing the
|
||||
execution:
|
||||
|
||||
2:0:1:1:1:50105669:1:0
|
||||
|
||||
Finally, loading the trace in the Paraver program, we can generate a
|
||||
timeline visualization of the state change. Here is the example for the
|
||||
same state transition of the thread stopping the execution:
|
||||
|
||||

|
62
doc/user/concepts/part-model.md
Normal file
62
doc/user/concepts/part-model.md
Normal file
@ -0,0 +1,62 @@
|
||||
# Part model
|
||||
|
||||
Ovni has a model to represent the hardware components as well as the software
|
||||
concepts like threads or processes. Each concept is considered to be a *part*.
|
||||
Here is an example diagram depicting the part hierarchy:
|
||||
|
||||

|
||||
|
||||
Notice how a loom can restrict the CPUs of the node to its child processes.
|
||||
|
||||
## Software parts
|
||||
|
||||
These are not physical parts, but they abstract common concepts.
|
||||
|
||||
### Thread
|
||||
|
||||
A thread in ovni is directly mapped to a [POSIX
|
||||
thread](https://en.wikipedia.org/wiki/Pthreads) and they are identified by a
|
||||
`TID` which must be unique in a [node](#node). Threads in ovni have [a model with
|
||||
an internal state](../emulation/ovni.md/#thread_model) that tries to tracks the
|
||||
state of the real thread.
|
||||
|
||||
### Process
|
||||
|
||||
A process is directly mapped to a UNIX
|
||||
[process](https://en.wikipedia.org/wiki/Process_(computing)) and they are
|
||||
identified by a `PID` number which must be unique in a [node](#node).
|
||||
|
||||
### Loom
|
||||
|
||||
A loom has no direct mapping to a usual concept. It consists of a set of
|
||||
[CPUs](#cpu) from the same node and a set of processes that can *only run in
|
||||
those CPUs*. Each CPUs must belong to one and only one loom. It is often used
|
||||
to group CPUs that belong to the same process when running workloads with
|
||||
multiple processes (like with MPI).
|
||||
|
||||
Each loom has a virtual CPU which collects running threads that are not
|
||||
exclusively assigned to a physical CPU, so we cannot determine on which CPU they
|
||||
are running.
|
||||
|
||||
## Hardware parts
|
||||
|
||||
These parts have a physical object assigned.
|
||||
|
||||
### CPU
|
||||
|
||||
A CPU is a hardware thread that can execute at most one thread at a time. Each
|
||||
CPU must have a physical ID that is unique in a node. In ovni there is also a
|
||||
virtual CPU, which simply is used to collect threads that are not tied to an
|
||||
specific physical CPU, so it cannot be easily determined where they are running.
|
||||
|
||||
### Node
|
||||
|
||||
A *node* refers to a compute node, often a physical machine with memory and
|
||||
network which may contain one or more
|
||||
[sockets](https://en.wikipedia.org/wiki/CPU_socket), where each socket has one
|
||||
or more CPUs.
|
||||
|
||||
### System
|
||||
|
||||
A system represents the complete set of hardware parts and software parts that
|
||||
are known to ovni in a given trace.
|
516
doc/user/concepts/part-model.svg
Normal file
516
doc/user/concepts/part-model.svg
Normal file
@ -0,0 +1,516 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
||||
|
||||
<svg
|
||||
width="306.91663mm"
|
||||
height="127.00001mm"
|
||||
viewBox="0 0 306.91663 127.00001"
|
||||
version="1.1"
|
||||
id="svg1"
|
||||
inkscape:version="1.3.2 (091e20ef0f, 2023-11-25, custom)"
|
||||
sodipodi:docname="part-model.svg"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:svg="http://www.w3.org/2000/svg">
|
||||
<sodipodi:namedview
|
||||
id="namedview1"
|
||||
pagecolor="#ffffff"
|
||||
bordercolor="#999999"
|
||||
borderopacity="1"
|
||||
inkscape:showpageshadow="0"
|
||||
inkscape:pageopacity="0"
|
||||
inkscape:pagecheckerboard="0"
|
||||
inkscape:deskcolor="#d1d1d1"
|
||||
inkscape:document-units="mm"
|
||||
showgrid="false"
|
||||
inkscape:zoom="0.73049944"
|
||||
inkscape:cx="629.70616"
|
||||
inkscape:cy="280.62992"
|
||||
inkscape:window-width="1920"
|
||||
inkscape:window-height="1031"
|
||||
inkscape:window-x="0"
|
||||
inkscape:window-y="24"
|
||||
inkscape:window-maximized="1"
|
||||
inkscape:current-layer="layer1">
|
||||
<inkscape:grid
|
||||
id="grid1"
|
||||
units="px"
|
||||
originx="-74.083327"
|
||||
originy="-89.958345"
|
||||
spacingx="0.26458333"
|
||||
spacingy="0.26458334"
|
||||
empcolor="#7a7aff"
|
||||
empopacity="0.25098039"
|
||||
color="#6a6aff"
|
||||
opacity="0.1254902"
|
||||
empspacing="5"
|
||||
dotted="false"
|
||||
gridanglex="30"
|
||||
gridanglez="30"
|
||||
visible="false" />
|
||||
</sodipodi:namedview>
|
||||
<defs
|
||||
id="defs1" />
|
||||
<g
|
||||
inkscape:label="Layer 1"
|
||||
inkscape:groupmode="layer"
|
||||
id="layer1"
|
||||
transform="translate(-74.083328,-89.958344)">
|
||||
<rect
|
||||
style="fill:none;stroke:none;stroke-width:0.264999;stroke-linecap:square;stroke-dasharray:none;stroke-dashoffset:0"
|
||||
id="rect33"
|
||||
width="306.91663"
|
||||
height="127.00001"
|
||||
x="74.083328"
|
||||
y="89.958344" />
|
||||
<rect
|
||||
style="fill:#ffeeaa;stroke:#000000;stroke-width:0.2;stroke-linecap:square;stroke-dasharray:none;stroke-dashoffset:0"
|
||||
id="rect32"
|
||||
width="121.70832"
|
||||
height="84.666679"
|
||||
x="253.99998"
|
||||
y="127.00001" />
|
||||
<text
|
||||
xml:space="preserve"
|
||||
style="font-size:6.35px;line-height:1.25;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;stroke-width:0.264583"
|
||||
x="290.19363"
|
||||
y="202.25124"
|
||||
id="text32"><tspan
|
||||
sodipodi:role="line"
|
||||
id="tspan32"
|
||||
style="font-size:6.35px;stroke-width:0.264583"
|
||||
x="290.19363"
|
||||
y="202.25124">Hardware parts</tspan></text>
|
||||
<rect
|
||||
style="fill:#eeffaa;stroke:#000000;stroke-width:0.2;stroke-linecap:square;stroke-dasharray:none;stroke-dashoffset:0"
|
||||
id="rect31"
|
||||
width="169.33333"
|
||||
height="84.666679"
|
||||
x="79.374992"
|
||||
y="127.00001" />
|
||||
<rect
|
||||
style="fill:#fff6d5;stroke:#000000;stroke-width:0.3;stroke-linecap:square;stroke-dasharray:none;stroke-dashoffset:0"
|
||||
id="rect27"
|
||||
width="47.625004"
|
||||
height="21.166676"
|
||||
x="269.87497"
|
||||
y="148.16667"
|
||||
ry="2.645834" />
|
||||
<rect
|
||||
style="fill:#ffffff;stroke:#000000;stroke-width:0.2;stroke-linecap:square;stroke-dasharray:none"
|
||||
id="rect1"
|
||||
width="21.166662"
|
||||
height="10.583329"
|
||||
x="84.566666"
|
||||
y="174.52502"
|
||||
ry="0" />
|
||||
<text
|
||||
xml:space="preserve"
|
||||
style="font-size:4.23333px;line-height:1.25;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;stroke-width:0.264583"
|
||||
x="87.947342"
|
||||
y="181.39488"
|
||||
id="text1"><tspan
|
||||
sodipodi:role="line"
|
||||
id="tspan1"
|
||||
style="stroke-width:0.264583"
|
||||
x="87.947342"
|
||||
y="181.39488">Thread</tspan></text>
|
||||
<rect
|
||||
style="fill:#ffffff;stroke:#000000;stroke-width:0.2;stroke-linecap:square;stroke-dasharray:none"
|
||||
id="rect3"
|
||||
width="21.166664"
|
||||
height="10.583337"
|
||||
x="111.12499"
|
||||
y="153.45834"
|
||||
ry="0" />
|
||||
<text
|
||||
xml:space="preserve"
|
||||
style="font-size:4.23333px;line-height:1.25;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;stroke-width:0.264583"
|
||||
x="113.57446"
|
||||
y="160.26309"
|
||||
id="text3"><tspan
|
||||
sodipodi:role="line"
|
||||
id="tspan3"
|
||||
style="stroke-width:0.264583"
|
||||
x="113.57446"
|
||||
y="160.26309">Process</tspan></text>
|
||||
<rect
|
||||
style="fill:#ffffff;stroke:#000000;stroke-width:0.2;stroke-linecap:square;stroke-dasharray:none"
|
||||
id="rect4"
|
||||
width="21.166664"
|
||||
height="10.583333"
|
||||
x="111.12499"
|
||||
y="174.62502"
|
||||
ry="0" />
|
||||
<text
|
||||
xml:space="preserve"
|
||||
style="font-size:4.23333px;line-height:1.25;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;stroke-width:0.264583"
|
||||
x="114.50567"
|
||||
y="181.49487"
|
||||
id="text4"><tspan
|
||||
sodipodi:role="line"
|
||||
id="tspan4"
|
||||
style="stroke-width:0.264583"
|
||||
x="114.50567"
|
||||
y="181.49487">Thread</tspan></text>
|
||||
<rect
|
||||
style="fill:#ffffff;stroke:#000000;stroke-width:0.2;stroke-linecap:square;stroke-dasharray:none"
|
||||
id="rect5"
|
||||
width="21.166658"
|
||||
height="10.583333"
|
||||
x="137.58333"
|
||||
y="174.62502"
|
||||
ry="0" />
|
||||
<text
|
||||
xml:space="preserve"
|
||||
style="font-size:4.23333px;line-height:1.25;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;stroke-width:0.264583"
|
||||
x="140.964"
|
||||
y="181.49487"
|
||||
id="text5"><tspan
|
||||
sodipodi:role="line"
|
||||
id="tspan5"
|
||||
style="stroke-width:0.264583"
|
||||
x="140.964"
|
||||
y="181.49487">Thread</tspan></text>
|
||||
<rect
|
||||
style="fill:#ffffff;stroke:#000000;stroke-width:0.2;stroke-linecap:square;stroke-dasharray:none"
|
||||
id="rect6"
|
||||
width="21.166672"
|
||||
height="10.583333"
|
||||
x="169.33331"
|
||||
y="174.62502"
|
||||
ry="0" />
|
||||
<text
|
||||
xml:space="preserve"
|
||||
style="font-size:4.23333px;line-height:1.25;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;stroke-width:0.264583"
|
||||
x="172.71399"
|
||||
y="181.49487"
|
||||
id="text6"><tspan
|
||||
sodipodi:role="line"
|
||||
id="tspan6"
|
||||
style="stroke-width:0.264583"
|
||||
x="172.71399"
|
||||
y="181.49487">Thread</tspan></text>
|
||||
<rect
|
||||
style="fill:#ffffff;stroke:#000000;stroke-width:0.2;stroke-linecap:square;stroke-dasharray:none"
|
||||
id="rect7"
|
||||
width="20.966642"
|
||||
height="10.38336"
|
||||
x="195.99167"
|
||||
y="153.55833"
|
||||
ry="0" />
|
||||
<text
|
||||
xml:space="preserve"
|
||||
style="font-size:4.23333px;line-height:1.25;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;stroke-width:0.264583"
|
||||
x="198.24112"
|
||||
y="160.16312"
|
||||
id="text7"><tspan
|
||||
sodipodi:role="line"
|
||||
id="tspan7"
|
||||
style="stroke-width:0.264583"
|
||||
x="198.24112"
|
||||
y="160.16312">Process</tspan></text>
|
||||
<rect
|
||||
style="fill:#ffffff;stroke:#000000;stroke-width:0.2;stroke-linecap:square;stroke-dasharray:none"
|
||||
id="rect8"
|
||||
width="21.16667"
|
||||
height="10.583333"
|
||||
x="195.79164"
|
||||
y="174.62502"
|
||||
ry="0" />
|
||||
<text
|
||||
xml:space="preserve"
|
||||
style="font-size:4.23333px;line-height:1.25;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;stroke-width:0.264583"
|
||||
x="199.17232"
|
||||
y="181.49487"
|
||||
id="text8"><tspan
|
||||
sodipodi:role="line"
|
||||
id="tspan8"
|
||||
style="stroke-width:0.264583"
|
||||
x="199.17232"
|
||||
y="181.49487">Thread</tspan></text>
|
||||
<rect
|
||||
style="fill:#ffffff;stroke:#000000;stroke-width:0.2;stroke-linecap:square;stroke-dasharray:none"
|
||||
id="rect9"
|
||||
width="21.166666"
|
||||
height="10.583333"
|
||||
x="222.24997"
|
||||
y="174.62502"
|
||||
ry="0" />
|
||||
<text
|
||||
xml:space="preserve"
|
||||
style="font-size:4.23333px;line-height:1.25;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;stroke-width:0.264583"
|
||||
x="225.63065"
|
||||
y="181.49487"
|
||||
id="text9"><tspan
|
||||
sodipodi:role="line"
|
||||
id="tspan9"
|
||||
style="stroke-width:0.264583"
|
||||
x="225.63065"
|
||||
y="181.49487">Thread</tspan></text>
|
||||
<rect
|
||||
style="fill:#ffffff;stroke:#000000;stroke-width:0.2;stroke-linecap:square;stroke-dasharray:none"
|
||||
id="rect10"
|
||||
width="21.166658"
|
||||
height="10.583337"
|
||||
x="153.45833"
|
||||
y="132.29167"
|
||||
ry="0" />
|
||||
<text
|
||||
xml:space="preserve"
|
||||
style="font-size:4.23333px;line-height:1.25;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;stroke-width:0.264583"
|
||||
x="158.21979"
|
||||
y="139.09644"
|
||||
id="text10"><tspan
|
||||
sodipodi:role="line"
|
||||
id="tspan10"
|
||||
style="stroke-width:0.264583"
|
||||
x="158.21979"
|
||||
y="139.09644">Loom</tspan></text>
|
||||
<rect
|
||||
style="fill:#ffffff;stroke:#000000;stroke-width:0.2;stroke-linecap:square;stroke-dasharray:none"
|
||||
id="rect11"
|
||||
width="21.166651"
|
||||
height="10.58334"
|
||||
x="306.91666"
|
||||
y="132.29167"
|
||||
ry="0" />
|
||||
<text
|
||||
xml:space="preserve"
|
||||
style="font-size:4.23333px;line-height:1.25;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;stroke-width:0.264583"
|
||||
x="311.88068"
|
||||
y="139.16154"
|
||||
id="text11"><tspan
|
||||
sodipodi:role="line"
|
||||
id="tspan11"
|
||||
style="stroke-width:0.264583"
|
||||
x="311.88068"
|
||||
y="139.16154">Node</tspan></text>
|
||||
<rect
|
||||
style="fill:#ffffff;stroke:#000000;stroke-width:0.2;stroke-linecap:square;stroke-dasharray:none"
|
||||
id="rect12"
|
||||
width="15.875001"
|
||||
height="10.583341"
|
||||
x="275.16666"
|
||||
y="153.45834"
|
||||
ry="0" />
|
||||
<text
|
||||
xml:space="preserve"
|
||||
style="font-size:4.23333px;line-height:1.25;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;stroke-width:0.264583"
|
||||
x="278.86566"
|
||||
y="160.291"
|
||||
id="text12"><tspan
|
||||
sodipodi:role="line"
|
||||
id="tspan12"
|
||||
style="stroke-width:0.264583"
|
||||
x="278.86566"
|
||||
y="160.291">CPU</tspan></text>
|
||||
<rect
|
||||
style="fill:#ffffff;stroke:#000000;stroke-width:0.2;stroke-linecap:square;stroke-dasharray:none"
|
||||
id="rect13"
|
||||
width="15.874983"
|
||||
height="10.583341"
|
||||
x="296.33334"
|
||||
y="153.45834"
|
||||
ry="0" />
|
||||
<text
|
||||
xml:space="preserve"
|
||||
style="font-size:4.23333px;line-height:1.25;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;stroke-width:0.264583"
|
||||
x="300.03235"
|
||||
y="160.291"
|
||||
id="text13"><tspan
|
||||
sodipodi:role="line"
|
||||
id="tspan13"
|
||||
style="stroke-width:0.264583"
|
||||
x="300.03235"
|
||||
y="160.291">CPU</tspan></text>
|
||||
<path
|
||||
style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
|
||||
d="m 121.70832,164.04168 0,10.58334"
|
||||
id="path15"
|
||||
inkscape:connector-type="polyline"
|
||||
inkscape:connector-curvature="0"
|
||||
inkscape:connection-start="#rect3"
|
||||
inkscape:connection-end="#rect4" />
|
||||
<path
|
||||
style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
|
||||
d="m 206.45046,163.94169 -0.0505,10.68333"
|
||||
id="path16"
|
||||
inkscape:connector-type="polyline"
|
||||
inkscape:connector-curvature="0"
|
||||
inkscape:connection-start="#rect7"
|
||||
inkscape:connection-end="#rect8" />
|
||||
<path
|
||||
style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
|
||||
d="m 212.94006,163.94169 13.30366,10.68333"
|
||||
id="path17"
|
||||
inkscape:connector-type="polyline"
|
||||
inkscape:connector-curvature="0"
|
||||
inkscape:connection-end="#rect9"
|
||||
inkscape:connection-start="#rect7" />
|
||||
<path
|
||||
style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
|
||||
d="m 199.96086,163.94169 -13.40463,10.68333"
|
||||
id="path18"
|
||||
inkscape:connector-type="polyline"
|
||||
inkscape:connector-curvature="0"
|
||||
inkscape:connection-start="#rect7"
|
||||
inkscape:connection-end="#rect6" />
|
||||
<path
|
||||
style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
|
||||
d="m 128.32291,164.04168 13.22917,10.58334"
|
||||
id="path19"
|
||||
inkscape:connector-type="polyline"
|
||||
inkscape:connector-curvature="0"
|
||||
inkscape:connection-start="#rect3"
|
||||
inkscape:connection-end="#rect5" />
|
||||
<path
|
||||
style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
|
||||
d="m 115.03723,164.04168 -13.21614,10.48334"
|
||||
id="path20"
|
||||
inkscape:connector-type="polyline"
|
||||
inkscape:connector-curvature="0"
|
||||
inkscape:connection-start="#rect3"
|
||||
inkscape:connection-end="#rect1" />
|
||||
<path
|
||||
style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
|
||||
d="m 153.45833,142.87501 -21.16667,10.58333"
|
||||
id="path21"
|
||||
inkscape:connector-type="polyline"
|
||||
inkscape:connector-curvature="0"
|
||||
inkscape:connection-start="#rect10"
|
||||
inkscape:connection-end="#rect3" />
|
||||
<path
|
||||
style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
|
||||
d="m 174.62499,142.86254 21.44212,10.69579"
|
||||
id="path22"
|
||||
inkscape:connector-type="polyline"
|
||||
inkscape:connector-curvature="0"
|
||||
inkscape:connection-start="#rect10"
|
||||
inkscape:connection-end="#rect7" />
|
||||
<path
|
||||
style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
|
||||
d="m 314.19269,142.87501 -6.61457,10.58333"
|
||||
id="path24"
|
||||
inkscape:connector-type="polyline"
|
||||
inkscape:connector-curvature="0"
|
||||
inkscape:connection-start="#rect11"
|
||||
inkscape:connection-end="#rect13" />
|
||||
<path
|
||||
style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
|
||||
d="M 308.90102,142.87501 291.04166,153.8654"
|
||||
id="path25"
|
||||
inkscape:connector-type="polyline"
|
||||
inkscape:connector-curvature="0"
|
||||
inkscape:connection-start="#rect11"
|
||||
inkscape:connection-end="#rect12" />
|
||||
<rect
|
||||
style="fill:#ffffff;stroke:#000000;stroke-width:0.2;stroke-linecap:square;stroke-dasharray:none"
|
||||
id="rect25"
|
||||
width="15.875013"
|
||||
height="10.583337"
|
||||
x="322.79163"
|
||||
y="153.45834"
|
||||
ry="0" />
|
||||
<text
|
||||
xml:space="preserve"
|
||||
style="font-size:4.23333px;line-height:1.25;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;stroke-width:0.264583"
|
||||
x="326.49063"
|
||||
y="160.291"
|
||||
id="text25"><tspan
|
||||
sodipodi:role="line"
|
||||
id="tspan25"
|
||||
style="stroke-width:0.264583"
|
||||
x="326.49063"
|
||||
y="160.291">CPU</tspan></text>
|
||||
<rect
|
||||
style="fill:#ffffff;stroke:#000000;stroke-width:0.2;stroke-linecap:square;stroke-dasharray:none"
|
||||
id="rect26"
|
||||
width="15.874991"
|
||||
height="10.583337"
|
||||
x="343.95831"
|
||||
y="153.45834"
|
||||
ry="0" />
|
||||
<text
|
||||
xml:space="preserve"
|
||||
style="font-size:4.23333px;line-height:1.25;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;stroke-width:0.264583"
|
||||
x="347.65732"
|
||||
y="160.291"
|
||||
id="text26"><tspan
|
||||
sodipodi:role="line"
|
||||
id="tspan26"
|
||||
style="stroke-width:0.264583"
|
||||
x="347.65732"
|
||||
y="160.291">CPU</tspan></text>
|
||||
<path
|
||||
style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
|
||||
d="m 320.80727,142.87501 6.61457,10.58333"
|
||||
id="path26"
|
||||
inkscape:connector-type="polyline"
|
||||
inkscape:connector-curvature="0"
|
||||
inkscape:connection-start="#rect11"
|
||||
inkscape:connection-end="#rect25" />
|
||||
<path
|
||||
style="display:inline;fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
|
||||
d="m 326.09894,142.87501 17.85937,10.99039"
|
||||
id="path27"
|
||||
inkscape:connector-type="polyline"
|
||||
inkscape:connector-curvature="0"
|
||||
inkscape:connection-start="#rect11"
|
||||
inkscape:connection-end="#rect26" />
|
||||
<path
|
||||
style="display:inline;fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:0.265;stroke-linecap:butt;stroke-linejoin:miter;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
|
||||
d="m 174.62499,139.70001 95.24998,19.05"
|
||||
id="path28"
|
||||
inkscape:connector-type="polyline"
|
||||
inkscape:connector-curvature="0"
|
||||
inkscape:connection-start="#rect10" />
|
||||
<rect
|
||||
style="fill:#ffffff;stroke:#000000;stroke-width:0.2;stroke-linecap:square;stroke-dasharray:none"
|
||||
id="rect29"
|
||||
width="21.166666"
|
||||
height="10.583335"
|
||||
x="216.95831"
|
||||
y="95.250008"
|
||||
ry="0" />
|
||||
<text
|
||||
xml:space="preserve"
|
||||
style="font-size:4.23333px;line-height:1.25;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;stroke-width:0.264583"
|
||||
x="219.6889"
|
||||
y="101.67236"
|
||||
id="text29"><tspan
|
||||
sodipodi:role="line"
|
||||
id="tspan29"
|
||||
style="stroke-width:0.264583"
|
||||
x="219.6889"
|
||||
y="101.67236">System</tspan></text>
|
||||
<path
|
||||
style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
|
||||
d="m 238.12498,104.89952 68.79168,28.32598"
|
||||
id="path29"
|
||||
inkscape:connector-type="polyline"
|
||||
inkscape:connector-curvature="0"
|
||||
inkscape:connection-start="#rect29"
|
||||
inkscape:connection-end="#rect11" />
|
||||
<path
|
||||
style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
|
||||
d="m 218.47022,105.83334 -45.35713,26.45833"
|
||||
id="path30"
|
||||
inkscape:connector-type="polyline"
|
||||
inkscape:connector-curvature="0"
|
||||
inkscape:connection-start="#rect29"
|
||||
inkscape:connection-end="#rect10" />
|
||||
<text
|
||||
xml:space="preserve"
|
||||
style="font-size:6.35px;line-height:1.25;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;stroke-width:0.264583"
|
||||
x="140.81671"
|
||||
y="202.25124"
|
||||
id="text31"><tspan
|
||||
sodipodi:role="line"
|
||||
id="tspan31"
|
||||
style="font-size:6.35px;stroke-width:0.264583"
|
||||
x="140.81671"
|
||||
y="202.25124">Software parts</tspan></text>
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 18 KiB |
123
doc/user/concepts/trace.md
Normal file
123
doc/user/concepts/trace.md
Normal file
@ -0,0 +1,123 @@
|
||||
# Trace concepts
|
||||
|
||||
When using libovni to generate traces or the emulator to process them, there are
|
||||
several concepts to keep in mind.
|
||||
|
||||
## Trace elements
|
||||
|
||||
The information generated by a program or later processed by other ovni tools is
|
||||
known as a trace. A runtime trace stores the information as-is in disk from a
|
||||
program execution. While a emulation trace is generated from the runtime trace
|
||||
for visualization with Paraver.
|
||||
|
||||
Both runtime and emulation traces are always stored inside the same directory,
|
||||
by default `ovni/`, which is known as the *trace directory*.
|
||||
|
||||
Here are the components of a runtime trace, as generated by libovni:
|
||||
|
||||
<p align="center">
|
||||
<img alt="Trace concepts" src="../trace.svg">
|
||||
</p>
|
||||
|
||||
### Stream
|
||||
|
||||
A stream is a directory which contains a binary stream and the associated stream
|
||||
metadata file. Each stream is associated with a given part of a system. As of
|
||||
now, libovni can only generate streams associated to [threads](part-model.md#thread).
|
||||
|
||||
### Stream metadata
|
||||
|
||||
The stream metadata is a JSON file named `stream.json` which holds information
|
||||
about the stream itself.
|
||||
|
||||
### Binary stream
|
||||
|
||||
A binary stream is a file named `stream.obs` (.obs stands for Ovni Binary
|
||||
Stream) composed of a header and a concatenated array of events without padding.
|
||||
Notice that each event may have different length.
|
||||
|
||||
### Event
|
||||
|
||||
An event is a point in time that has some information associated. Events written
|
||||
at runtime by libovni have at MCV, a clock and a optional payload. The list of
|
||||
all events recognized by the emulator can be found [here](../emulation/events.md).
|
||||
|
||||
Events can be displayed by ovnidump, which shows an explanation of what the
|
||||
event means:
|
||||
|
||||
```txt
|
||||
$ ovnidump ovni/loom.hop.nosv-u1000/proc.1121064 | grep -A 10 VTx | head
|
||||
517267929632815 VTx thread.1121064 executes the task 1 with bodyid 0
|
||||
517267930261672 VYc thread.1121064 creates task type 2 with label "task"
|
||||
517267930875858 VTC thread.1121064 creates parallel task 2 with type 2
|
||||
517267930877789 VU[ thread.1121064 starts submitting a task
|
||||
517267930877990 VU] thread.1121064 stops submitting a task
|
||||
517267930878098 VTC thread.1121064 creates parallel task 3 with type 2
|
||||
517267930878196 VU[ thread.1121064 starts submitting a task
|
||||
517267930878349 VU] thread.1121064 stops submitting a task
|
||||
517267930878432 VTC thread.1121064 creates parallel task 4 with type 2
|
||||
517267930878494 VU[ thread.1121064 starts submitting a task
|
||||
```
|
||||
|
||||
There are two types or events: normal and jumbo events, the latter can hold
|
||||
large attached payloads.
|
||||
|
||||
### MCV
|
||||
|
||||
The MCV acronym is the abbreviation of Model-Class-Value, which are a three
|
||||
characters that identify any event. The MCV is shown in the ovnitop and ovnidump
|
||||
tools and allows easy filtering with grep, for a single or related events:
|
||||
|
||||
```
|
||||
$ ovnitop ovni | grep VT
|
||||
VTe 20002
|
||||
VTx 20002
|
||||
VTC 200
|
||||
VTc 2
|
||||
VTp 1
|
||||
VTr 1
|
||||
```
|
||||
|
||||
### Clock
|
||||
|
||||
A clock is a 64 bit counter, which counts the number of nanoseconds from an
|
||||
arbitrary point in time in the past. Each event has the value of the clock
|
||||
stored inside, to indicate when that event happened. In a given trace there can
|
||||
be multiple clocks which don't refer to the same point in the past and must be
|
||||
corrected so they all produce an ordered sequence of events. The ovnisync
|
||||
program performs this correction by measuring the difference across clocks of
|
||||
different nodes.
|
||||
|
||||
### Payload
|
||||
|
||||
Events may have associated additional information which is stored in the stream.
|
||||
Normal events can hold up to 16 bytes, otherwise the jumbo events must be used
|
||||
to hold additional payload.
|
||||
|
||||
## Other related concepts
|
||||
|
||||
Apart from the trace itself, there are other concepts to keep in mind when the
|
||||
trace is being processed by the emulator.
|
||||
|
||||
### Event model
|
||||
|
||||
Each event belongs to an event model, as identified by the model character in
|
||||
the MCV. An event model is composed of several components:
|
||||
|
||||
- A set of [events](#event) all with the same model identifier in the
|
||||
[MCV](#mcv)
|
||||
- The emulator code that processes those events.
|
||||
- A human readable name, like `ovni` or `nanos6`.
|
||||
- A semantic version.
|
||||
|
||||
### State
|
||||
|
||||
A state is a discrete value that can change over time based on the events the
|
||||
emulator receives. Usually a single event causes a single state change, which is
|
||||
then written to the Paraver traces. An example is the thread state, which can
|
||||
change over time based on the events `OH*` that indicate a state transition
|
||||
of the current thread.
|
||||
|
||||
In contrast with an event, states have a duration associated which can usually
|
||||
be observed in Paraver. Notice that the trace only contains events, the states
|
||||
are computed at emulation.
|
474
doc/user/concepts/trace.svg
Normal file
474
doc/user/concepts/trace.svg
Normal file
@ -0,0 +1,474 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
||||
|
||||
<svg
|
||||
width="116.41666mm"
|
||||
height="105.83334mm"
|
||||
viewBox="0 0 116.41666 105.83334"
|
||||
version="1.1"
|
||||
id="svg1"
|
||||
inkscape:version="1.3.2 (091e20ef0f, 2023-11-25, custom)"
|
||||
sodipodi:docname="trace.svg"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:svg="http://www.w3.org/2000/svg">
|
||||
<sodipodi:namedview
|
||||
id="namedview1"
|
||||
pagecolor="#ffffff"
|
||||
bordercolor="#999999"
|
||||
borderopacity="1"
|
||||
inkscape:showpageshadow="0"
|
||||
inkscape:pageopacity="0"
|
||||
inkscape:pagecheckerboard="0"
|
||||
inkscape:deskcolor="#d1d1d1"
|
||||
inkscape:document-units="mm"
|
||||
showgrid="true"
|
||||
inkscape:zoom="1.4609989"
|
||||
inkscape:cx="248.80238"
|
||||
inkscape:cy="199.17879"
|
||||
inkscape:window-width="1920"
|
||||
inkscape:window-height="1031"
|
||||
inkscape:window-x="0"
|
||||
inkscape:window-y="24"
|
||||
inkscape:window-maximized="1"
|
||||
inkscape:current-layer="layer1">
|
||||
<inkscape:grid
|
||||
id="grid1"
|
||||
units="px"
|
||||
originx="-39.687498"
|
||||
originy="-15.875"
|
||||
spacingx="0.26458333"
|
||||
spacingy="0.26458334"
|
||||
empcolor="#7a7aff"
|
||||
empopacity="0.25098039"
|
||||
color="#6a6aff"
|
||||
opacity="0.1254902"
|
||||
empspacing="5"
|
||||
dotted="false"
|
||||
gridanglex="30"
|
||||
gridanglez="30"
|
||||
visible="true" />
|
||||
</sodipodi:namedview>
|
||||
<defs
|
||||
id="defs1">
|
||||
<rect
|
||||
x="45"
|
||||
y="370"
|
||||
width="209.99998"
|
||||
height="35"
|
||||
id="rect15" />
|
||||
</defs>
|
||||
<g
|
||||
inkscape:label="Layer 1"
|
||||
inkscape:groupmode="layer"
|
||||
id="layer1"
|
||||
transform="translate(-39.687498,-15.875)">
|
||||
<g
|
||||
id="g59"
|
||||
transform="translate(5.5781353,-0.74017783)"
|
||||
style="stroke-width:0.15875;stroke-dasharray:none">
|
||||
<text
|
||||
xml:space="preserve"
|
||||
style="font-size:4.23333px;line-height:1.25;font-family:sans-serif;text-align:center;letter-spacing:0px;word-spacing:0px;text-anchor:middle;stroke-width:0.15875;stroke-dasharray:none"
|
||||
x="100.23113"
|
||||
y="24.896034"
|
||||
id="text22"><tspan
|
||||
sodipodi:role="line"
|
||||
id="tspan22"
|
||||
style="text-align:center;text-anchor:middle;stroke-width:0.15875;stroke-dasharray:none"
|
||||
x="100.23113"
|
||||
y="24.896034">Trace</tspan></text>
|
||||
<rect
|
||||
style="fill:none;stroke:#000000;stroke-width:0.15875;stroke-linecap:square;stroke-dasharray:none"
|
||||
id="rect32"
|
||||
width="15.875"
|
||||
height="7.9375005"
|
||||
x="92.281235"
|
||||
y="19.4142" />
|
||||
</g>
|
||||
<g
|
||||
id="g57"
|
||||
transform="translate(-0.84260657,-0.98969722)"
|
||||
style="stroke-width:0.15875;stroke-dasharray:none">
|
||||
<text
|
||||
xml:space="preserve"
|
||||
style="font-size:4.23333px;line-height:1.25;font-family:sans-serif;text-align:center;letter-spacing:0px;word-spacing:0px;text-anchor:middle;stroke-width:0.15875;stroke-dasharray:none"
|
||||
x="106.39689"
|
||||
y="46.186943"
|
||||
id="text24"><tspan
|
||||
sodipodi:role="line"
|
||||
id="tspan24"
|
||||
style="text-align:center;text-anchor:middle;stroke-width:0.15875;stroke-dasharray:none"
|
||||
x="106.39689"
|
||||
y="46.186943">Stream</tspan></text>
|
||||
<rect
|
||||
style="fill:none;stroke:#000000;stroke-width:0.15875;stroke-linecap:square;stroke-dasharray:none"
|
||||
id="rect33"
|
||||
width="21.166666"
|
||||
height="7.9375019"
|
||||
x="96.092606"
|
||||
y="40.6772" />
|
||||
</g>
|
||||
<g
|
||||
id="g56"
|
||||
transform="translate(0.32292488,-0.89336269)"
|
||||
style="stroke-width:0.15875;stroke-dasharray:none">
|
||||
<text
|
||||
xml:space="preserve"
|
||||
style="font-size:4.23333px;line-height:1.25;font-family:sans-serif;text-align:center;letter-spacing:0px;word-spacing:0px;text-anchor:middle;stroke-width:0.15875;stroke-dasharray:none"
|
||||
x="78.773026"
|
||||
y="46.090603"
|
||||
id="text23"><tspan
|
||||
sodipodi:role="line"
|
||||
id="tspan23"
|
||||
style="text-align:center;text-anchor:middle;stroke-width:0.15875;stroke-dasharray:none"
|
||||
x="78.773026"
|
||||
y="46.090603">Stream</tspan></text>
|
||||
<rect
|
||||
style="fill:none;stroke:#000000;stroke-width:0.15875;stroke-linecap:square;stroke-dasharray:none"
|
||||
id="rect34"
|
||||
width="21.166666"
|
||||
height="7.9375"
|
||||
x="68.468742"
|
||||
y="40.580864" />
|
||||
</g>
|
||||
<path
|
||||
style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:0.15875;stroke-linecap:butt;stroke-linejoin:miter;stroke-dasharray:none;stroke-opacity:1"
|
||||
d="M 100.80665,26.611522 84.365216,39.687501"
|
||||
id="path35"
|
||||
inkscape:connector-type="polyline"
|
||||
inkscape:connector-curvature="0"
|
||||
inkscape:connection-start="#rect32"
|
||||
inkscape:connection-end="#rect34" />
|
||||
<path
|
||||
style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:0.15875;stroke-linecap:butt;stroke-linejoin:miter;stroke-dasharray:none;stroke-opacity:1"
|
||||
d="m 105.80376,26.611522 0.0227,13.075981"
|
||||
id="path36"
|
||||
inkscape:connector-type="polyline"
|
||||
inkscape:connector-curvature="0"
|
||||
inkscape:connection-start="#rect32"
|
||||
inkscape:connection-end="#rect33" />
|
||||
<g
|
||||
id="g54"
|
||||
style="stroke-width:0.15875;stroke-dasharray:none"
|
||||
transform="translate(-0.99999828,-3.5391978)">
|
||||
<text
|
||||
xml:space="preserve"
|
||||
style="font-size:4.23333px;line-height:1.25;font-family:sans-serif;text-align:center;letter-spacing:0px;word-spacing:0px;text-anchor:middle;stroke-width:0.15875;stroke-dasharray:none"
|
||||
x="58.792854"
|
||||
y="72.586143"
|
||||
id="text25"><tspan
|
||||
sodipodi:role="line"
|
||||
id="tspan25"
|
||||
style="text-align:center;text-anchor:middle;stroke-width:0.15875;stroke-dasharray:none"
|
||||
x="58.792854"
|
||||
y="72.586143">Metadata</tspan></text>
|
||||
<rect
|
||||
style="fill:none;stroke:#000000;stroke-width:0.15875;stroke-linecap:square;stroke-dasharray:none"
|
||||
id="rect36"
|
||||
width="26.458336"
|
||||
height="7.9375"
|
||||
x="45.979164"
|
||||
y="67.0392" />
|
||||
</g>
|
||||
<g
|
||||
id="g55"
|
||||
style="stroke-width:0.15875;stroke-dasharray:none"
|
||||
transform="translate(-0.99999828,-3.5391978)">
|
||||
<text
|
||||
xml:space="preserve"
|
||||
style="font-size:4.23333px;line-height:1.25;font-family:sans-serif;text-align:center;letter-spacing:0px;word-spacing:0px;text-anchor:middle;stroke-width:0.15875;stroke-dasharray:none"
|
||||
x="97.157433"
|
||||
y="72.175835"
|
||||
id="text26"><tspan
|
||||
sodipodi:role="line"
|
||||
id="tspan26"
|
||||
style="text-align:center;text-anchor:middle;stroke-width:0.15875;stroke-dasharray:none"
|
||||
x="97.157433"
|
||||
y="72.175835">Binary stream</tspan></text>
|
||||
<rect
|
||||
style="fill:none;stroke:#000000;stroke-width:0.15875;stroke-linecap:square;stroke-dasharray:none"
|
||||
id="rect37"
|
||||
width="34.395832"
|
||||
height="7.9375"
|
||||
x="80.374992"
|
||||
y="67.0392" />
|
||||
</g>
|
||||
<rect
|
||||
style="stroke-width:0.15875;stroke-dasharray:none;fill:none;stroke:none;stroke-linecap:square"
|
||||
id="rect60"
|
||||
width="116.41666"
|
||||
height="105.83334"
|
||||
x="40.687496"
|
||||
y="19.414198"
|
||||
inkscape:label="background"
|
||||
transform="translate(-0.99999735,-3.5391979)" />
|
||||
<path
|
||||
style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:0.15875;stroke-linecap:butt;stroke-linejoin:miter;stroke-dasharray:none;stroke-opacity:1"
|
||||
d="M 75.847223,47.625001 61.736111,63.500002"
|
||||
id="path37"
|
||||
inkscape:connector-type="polyline"
|
||||
inkscape:connector-curvature="0"
|
||||
inkscape:connection-start="#rect34"
|
||||
inkscape:connection-end="#rect36" />
|
||||
<path
|
||||
style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:0.15875;stroke-linecap:butt;stroke-linejoin:miter;stroke-dasharray:none;stroke-opacity:1"
|
||||
d="M 82.241318,47.625001 93.706592,63.500002"
|
||||
id="path38"
|
||||
inkscape:connector-type="polyline"
|
||||
inkscape:connector-curvature="0"
|
||||
inkscape:connection-start="#rect34"
|
||||
inkscape:connection-end="#rect37" />
|
||||
<g
|
||||
id="g53"
|
||||
style="stroke-width:0.15875;stroke-dasharray:none"
|
||||
transform="translate(-8.2507438,-3.0742007)">
|
||||
<text
|
||||
xml:space="preserve"
|
||||
style="font-size:4.23333px;line-height:1.25;font-family:sans-serif;text-align:center;letter-spacing:0px;word-spacing:0px;text-anchor:middle;stroke-width:0.15875;stroke-dasharray:none"
|
||||
x="62.074863"
|
||||
y="95.933655"
|
||||
id="text27"><tspan
|
||||
sodipodi:role="line"
|
||||
id="tspan27"
|
||||
style="text-align:center;text-anchor:middle;stroke-width:0.15875;stroke-dasharray:none"
|
||||
x="62.074863"
|
||||
y="95.933655">Header</tspan></text>
|
||||
<rect
|
||||
style="fill:none;stroke:#000000;stroke-width:0.15875;stroke-linecap:square;stroke-dasharray:none"
|
||||
id="rect38"
|
||||
width="23.812506"
|
||||
height="7.9375014"
|
||||
x="50.584076"
|
||||
y="90.386703" />
|
||||
</g>
|
||||
<g
|
||||
id="g52"
|
||||
transform="translate(-8.2507438,-3.3066989)"
|
||||
style="stroke-width:0.15875;stroke-dasharray:none">
|
||||
<text
|
||||
xml:space="preserve"
|
||||
style="font-size:4.23333px;line-height:1.25;font-family:sans-serif;text-align:center;letter-spacing:0px;word-spacing:0px;text-anchor:middle;stroke-width:0.15875;stroke-dasharray:none"
|
||||
x="91.411552"
|
||||
y="96.101036"
|
||||
id="text28"><tspan
|
||||
sodipodi:role="line"
|
||||
id="tspan28"
|
||||
style="text-align:center;text-anchor:middle;stroke-width:0.15875;stroke-dasharray:none"
|
||||
x="91.411552"
|
||||
y="96.101036">Event</tspan></text>
|
||||
<rect
|
||||
style="fill:none;stroke:#000000;stroke-width:0.15875;stroke-linecap:square;stroke-dasharray:none"
|
||||
id="rect39"
|
||||
width="23.812506"
|
||||
height="7.9375014"
|
||||
x="79.920769"
|
||||
y="90.619202" />
|
||||
</g>
|
||||
<g
|
||||
id="g51"
|
||||
style="stroke-width:0.15875;stroke-dasharray:none"
|
||||
transform="translate(-8.2507438,-3.0742007)">
|
||||
<text
|
||||
xml:space="preserve"
|
||||
style="font-size:4.23333px;line-height:1.25;font-family:sans-serif;text-align:center;letter-spacing:0px;word-spacing:0px;text-anchor:middle;stroke-width:0.15875;stroke-dasharray:none"
|
||||
x="120.28321"
|
||||
y="95.868538"
|
||||
id="text29"><tspan
|
||||
sodipodi:role="line"
|
||||
id="tspan29"
|
||||
style="text-align:center;text-anchor:middle;stroke-width:0.15875;stroke-dasharray:none"
|
||||
x="120.28321"
|
||||
y="95.868538">Event</tspan></text>
|
||||
<rect
|
||||
style="fill:none;stroke:#000000;stroke-width:0.15875;stroke-linecap:square;stroke-dasharray:none"
|
||||
id="rect40"
|
||||
width="23.812506"
|
||||
height="7.9375014"
|
||||
x="108.79243"
|
||||
y="90.386703" />
|
||||
</g>
|
||||
<path
|
||||
style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:0.15875;stroke-linecap:butt;stroke-linejoin:miter;stroke-dasharray:none;stroke-opacity:1"
|
||||
d="M 89.517356,71.437502 61.29514,87.312503"
|
||||
id="path40"
|
||||
inkscape:connector-type="polyline"
|
||||
inkscape:connector-curvature="0"
|
||||
inkscape:connection-start="#rect37"
|
||||
inkscape:connection-end="#rect38" />
|
||||
<path
|
||||
style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:0.15875;stroke-linecap:butt;stroke-linejoin:miter;stroke-dasharray:none;stroke-opacity:1"
|
||||
d="M 94.406805,71.437502 85.742383,87.312503"
|
||||
id="path41"
|
||||
inkscape:connector-type="polyline"
|
||||
inkscape:connector-curvature="0"
|
||||
inkscape:connection-start="#rect37"
|
||||
inkscape:connection-end="#rect39" />
|
||||
<path
|
||||
style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:0.15875;stroke-linecap:butt;stroke-linejoin:miter;stroke-dasharray:none;stroke-opacity:1"
|
||||
d="M 99.218748,71.437502 109.8021,87.312503"
|
||||
id="path42"
|
||||
inkscape:connector-type="polyline"
|
||||
inkscape:connector-curvature="0"
|
||||
inkscape:connection-start="#rect37"
|
||||
inkscape:connection-end="#rect40" />
|
||||
<g
|
||||
id="g46"
|
||||
transform="translate(-68.659367,2.7781237)"
|
||||
style="stroke-width:0.15875;stroke-dasharray:none">
|
||||
<text
|
||||
xml:space="preserve"
|
||||
style="font-size:4.23333px;line-height:1.25;font-family:sans-serif;text-align:center;letter-spacing:0px;word-spacing:0px;text-anchor:middle;stroke-width:0.15875;stroke-dasharray:none"
|
||||
x="123.93869"
|
||||
y="113.98891"
|
||||
id="text30"><tspan
|
||||
sodipodi:role="line"
|
||||
id="tspan30"
|
||||
style="text-align:center;text-anchor:middle;stroke-width:0.15875;stroke-dasharray:none"
|
||||
x="123.93869"
|
||||
y="113.98891">MCV</tspan></text>
|
||||
<rect
|
||||
style="fill:none;stroke:#000000;stroke-width:0.15875;stroke-linecap:square;stroke-dasharray:none"
|
||||
id="rect42"
|
||||
width="15.875001"
|
||||
height="7.9375038"
|
||||
x="116.41666"
|
||||
y="108.47917" />
|
||||
</g>
|
||||
<g
|
||||
id="g45"
|
||||
transform="translate(-68.659367,5.4239515)"
|
||||
style="stroke-width:0.15875;stroke-dasharray:none">
|
||||
<text
|
||||
xml:space="preserve"
|
||||
style="font-size:4.23333px;line-height:1.25;font-family:sans-serif;text-align:center;letter-spacing:0px;word-spacing:0px;text-anchor:middle;stroke-width:0.15875;stroke-dasharray:none"
|
||||
x="147.92894"
|
||||
y="111.38029"
|
||||
id="text31"><tspan
|
||||
sodipodi:role="line"
|
||||
id="tspan31"
|
||||
style="text-align:center;text-anchor:middle;stroke-width:0.15875;stroke-dasharray:none"
|
||||
x="147.92894"
|
||||
y="111.38029">Clock</tspan></text>
|
||||
<rect
|
||||
style="fill:none;stroke:#000000;stroke-width:0.15875;stroke-linecap:square;stroke-dasharray:none"
|
||||
id="rect43"
|
||||
width="15.875001"
|
||||
height="7.9375038"
|
||||
x="140.22916"
|
||||
y="105.83334" />
|
||||
</g>
|
||||
<g
|
||||
id="g44"
|
||||
transform="translate(-66.112857,13.610614)"
|
||||
style="stroke-width:0.15875;stroke-dasharray:none">
|
||||
<g
|
||||
id="g60"
|
||||
style="stroke-width:0.15875;stroke-dasharray:none">
|
||||
<text
|
||||
xml:space="preserve"
|
||||
style="font-size:4.23333px;line-height:1.25;font-family:sans-serif;text-align:center;letter-spacing:0px;word-spacing:0px;text-anchor:middle;stroke-width:0.15875;stroke-dasharray:none"
|
||||
x="172.85362"
|
||||
y="102.65102"
|
||||
id="text32"><tspan
|
||||
sodipodi:role="line"
|
||||
id="tspan32"
|
||||
style="text-align:center;text-anchor:middle;stroke-width:0.15875;stroke-dasharray:none"
|
||||
x="172.85362"
|
||||
y="102.65102">Payload</tspan></text>
|
||||
<rect
|
||||
style="fill:none;stroke:#000000;stroke-width:0.15875;stroke-linecap:square;stroke-dasharray:none"
|
||||
id="rect44"
|
||||
width="23.812506"
|
||||
height="7.9375014"
|
||||
x="161.36285"
|
||||
y="97.514389" />
|
||||
</g>
|
||||
</g>
|
||||
<path
|
||||
style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:0.15875;stroke-linecap:butt;stroke-linejoin:miter;stroke-dasharray:none;stroke-opacity:1"
|
||||
d="M 83.366344,95.250004 80.27399,111.2573"
|
||||
id="path47"
|
||||
inkscape:connector-type="polyline"
|
||||
inkscape:connector-curvature="0"
|
||||
inkscape:connection-end="#g45" />
|
||||
<path
|
||||
style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:0.15875;stroke-linecap:butt;stroke-linejoin:miter;stroke-dasharray:none;stroke-opacity:1"
|
||||
d="M 80.082847,95.250004 60.540101,111.2573"
|
||||
id="path48"
|
||||
inkscape:connector-type="polyline"
|
||||
inkscape:connector-curvature="0"
|
||||
inkscape:connection-end="#g46" />
|
||||
<g
|
||||
id="g58"
|
||||
transform="translate(-1.9073076,-1.4153422)"
|
||||
style="stroke-width:0.15875;stroke-dasharray:none">
|
||||
<text
|
||||
xml:space="preserve"
|
||||
style="font-size:4.23333px;line-height:1.25;font-family:sans-serif;text-align:center;letter-spacing:0px;word-spacing:0px;text-anchor:middle;stroke-width:0.15875;stroke-dasharray:none"
|
||||
x="132.47266"
|
||||
y="46.496338"
|
||||
id="text49"><tspan
|
||||
sodipodi:role="line"
|
||||
id="tspan49"
|
||||
style="text-align:center;text-anchor:middle;stroke-width:0.15875;stroke-dasharray:none"
|
||||
x="132.47266"
|
||||
y="46.496338">...</tspan></text>
|
||||
<rect
|
||||
style="fill:none;stroke:#000000;stroke-width:0.15875;stroke-linecap:square;stroke-dasharray:none"
|
||||
id="rect49"
|
||||
width="18.619415"
|
||||
height="7.9374995"
|
||||
x="123.61564"
|
||||
y="41.102844" />
|
||||
</g>
|
||||
<path
|
||||
style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:0.15875;stroke-linecap:butt;stroke-linejoin:miter;stroke-dasharray:none;stroke-opacity:1"
|
||||
d="m 110.56031,26.611522 15.69429,13.07598"
|
||||
id="path49"
|
||||
inkscape:connector-type="polyline"
|
||||
inkscape:connector-curvature="0"
|
||||
inkscape:connection-end="#rect49"
|
||||
inkscape:connection-start="#rect32" />
|
||||
<g
|
||||
id="g50"
|
||||
transform="translate(-8.2507438,-2.9293636)"
|
||||
style="stroke-width:0.15875;stroke-dasharray:none">
|
||||
<text
|
||||
xml:space="preserve"
|
||||
style="font-size:4.23333px;line-height:1.25;font-family:sans-serif;text-align:center;letter-spacing:0px;word-spacing:0px;text-anchor:middle;stroke-width:0.15875;stroke-dasharray:none"
|
||||
x="149.35014"
|
||||
y="95.635361"
|
||||
id="text50"><tspan
|
||||
sodipodi:role="line"
|
||||
id="tspan50"
|
||||
style="text-align:center;text-anchor:middle;stroke-width:0.15875;stroke-dasharray:none"
|
||||
x="149.35014"
|
||||
y="95.635361">...</tspan></text>
|
||||
<rect
|
||||
style="fill:none;stroke:#000000;stroke-width:0.15875;stroke-linecap:square;stroke-dasharray:none"
|
||||
id="rect40-5"
|
||||
width="23.812506"
|
||||
height="7.9375014"
|
||||
x="137.89658"
|
||||
y="90.241867" />
|
||||
</g>
|
||||
<path
|
||||
style="display:inline;fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:0.15875;stroke-linecap:butt;stroke-linejoin:miter;stroke-dasharray:none;stroke-opacity:1"
|
||||
d="m 104.06944,71.437502 29.98612,15.875001"
|
||||
id="path50"
|
||||
inkscape:connector-type="polyline"
|
||||
inkscape:connector-curvature="0"
|
||||
inkscape:connection-start="#rect37"
|
||||
inkscape:connection-end="#g50" />
|
||||
<path
|
||||
style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:0.15875;stroke-linecap:butt;stroke-linejoin:miter;stroke-dasharray:none;stroke-opacity:1"
|
||||
d="M 87.506274,95.250004 103.22625,111.125"
|
||||
id="path60"
|
||||
inkscape:connector-type="polyline"
|
||||
inkscape:connector-curvature="0"
|
||||
inkscape:connection-start="#g52"
|
||||
inkscape:connection-end="#g44" />
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 19 KiB |
@ -1,7 +1,7 @@
|
||||
# Emulator events
|
||||
|
||||
This is a exhaustive list of the events recognized by the emulator.
|
||||
Built on Mar 20 2024.
|
||||
Built on Nov 13 2024.
|
||||
|
||||
## Model nanos6
|
||||
|
||||
@ -395,7 +395,7 @@ List of events for the model *mpi* with identifier **`M`** at version `1.0.0`:
|
||||
|
||||
## Model ovni
|
||||
|
||||
List of events for the model *ovni* with identifier **`O`** at version `1.0.0`:
|
||||
List of events for the model *ovni* with identifier **`O`** at version `1.1.0`:
|
||||
<dl>
|
||||
<dt><a id="OAr" href="#OAr"><pre>OAr(i32 cpu, i32 tid)</pre></a></dt>
|
||||
<dd>changes the affinity of thread %{tid} to CPU %{cpu}</dd>
|
||||
@ -427,11 +427,17 @@ List of events for the model *ovni* with identifier **`O`** at version `1.0.0`:
|
||||
<dd>enters unordered event region</dd>
|
||||
<dt><a id="OU]" href="#OU]"><pre>OU]</pre></a></dt>
|
||||
<dd>leaves unordered event region</dd>
|
||||
<dt><a id="OM[" href="#OM["><pre>OM[(i64 value, i32 type)</pre></a></dt>
|
||||
<dd>push mark with value %{value} from type %{type}</dd>
|
||||
<dt><a id="OM]" href="#OM]"><pre>OM](i64 value, i32 type)</pre></a></dt>
|
||||
<dd>pop mark with value %{value} from type %{type}</dd>
|
||||
<dt><a id="OM=" href="#OM="><pre>OM=(i64 value, i32 type)</pre></a></dt>
|
||||
<dd>set mark with value %{value} from type %{type}</dd>
|
||||
</dl>
|
||||
|
||||
## Model openmp
|
||||
|
||||
List of events for the model *openmp* with identifier **`P`** at version `1.1.0`:
|
||||
List of events for the model *openmp* with identifier **`P`** at version `1.2.0`:
|
||||
<dl>
|
||||
<dt><a id="PBb" href="#PBb"><pre>PBb</pre></a></dt>
|
||||
<dd>begins plain barrier</dd>
|
||||
@ -557,6 +563,18 @@ List of events for the model *openmp* with identifier **`P`** at version `1.1.0`
|
||||
<dd>begins initialization</dd>
|
||||
<dt><a id="PCI" href="#PCI"><pre>PCI</pre></a></dt>
|
||||
<dd>ceases initialization</dd>
|
||||
<dt><a id="POc" href="#POc"><pre>POc+(u32 typeid, str label)</pre></a></dt>
|
||||
<dd>creates a type %{typeid} with label "%{label}"</dd>
|
||||
<dt><a id="PPc" href="#PPc"><pre>PPc(u32 taskid, u32 typeid)</pre></a></dt>
|
||||
<dd>creates the task %{taskid} with type %{typeid}</dd>
|
||||
<dt><a id="PPx" href="#PPx"><pre>PPx(u32 taskid)</pre></a></dt>
|
||||
<dd>executes the task %{taskid}</dd>
|
||||
<dt><a id="PPe" href="#PPe"><pre>PPe(u32 taskid)</pre></a></dt>
|
||||
<dd>ends the task %{taskid}</dd>
|
||||
<dt><a id="PQx" href="#PQx"><pre>PQx(u32 typeid)</pre></a></dt>
|
||||
<dd>begins worksharing with type %{typeid}</dd>
|
||||
<dt><a id="PQe" href="#PQe"><pre>PQe(u32 typeid)</pre></a></dt>
|
||||
<dd>ends worksharing with type %{typeid}</dd>
|
||||
</dl>
|
||||
|
||||
## Model tampi
|
||||
@ -615,7 +633,7 @@ List of events for the model *tampi* with identifier **`T`** at version `1.0.0`:
|
||||
|
||||
## Model nosv
|
||||
|
||||
List of events for the model *nosv* with identifier **`V`** at version `2.1.0`:
|
||||
List of events for the model *nosv* with identifier **`V`** at version `2.4.0`:
|
||||
<dl>
|
||||
<dt><a id="VTc" href="#VTc"><pre>VTc(u32 taskid, u32 typeid)</pre></a></dt>
|
||||
<dd>creates task %{taskid} with type %{typeid}</dd>
|
||||
@ -709,6 +727,18 @@ List of events for the model *nosv* with identifier **`V`** at version `2.1.0`:
|
||||
<dd>enters nosv_barrier_wait()</dd>
|
||||
<dt><a id="VAB" href="#VAB"><pre>VAB</pre></a></dt>
|
||||
<dd>leaves nosv_barrier_wait()</dd>
|
||||
<dt><a id="VAo" href="#VAo"><pre>VAo</pre></a></dt>
|
||||
<dd>enters nosv_cond_wait()</dd>
|
||||
<dt><a id="VAO" href="#VAO"><pre>VAO</pre></a></dt>
|
||||
<dd>leaves nosv_cond_wait()</dd>
|
||||
<dt><a id="VAg" href="#VAg"><pre>VAg</pre></a></dt>
|
||||
<dd>enters nosv_cond_signal()</dd>
|
||||
<dt><a id="VAG" href="#VAG"><pre>VAG</pre></a></dt>
|
||||
<dd>leaves nosv_cond_signal()</dd>
|
||||
<dt><a id="VAk" href="#VAk"><pre>VAk</pre></a></dt>
|
||||
<dd>enters nosv_cond_broadcast()</dd>
|
||||
<dt><a id="VAK" href="#VAK"><pre>VAK</pre></a></dt>
|
||||
<dd>leaves nosv_cond_broadcast()</dd>
|
||||
<dt><a id="VHa" href="#VHa"><pre>VHa</pre></a></dt>
|
||||
<dd>enters nosv_attach()</dd>
|
||||
<dt><a id="VHA" href="#VHA"><pre>VHA</pre></a></dt>
|
||||
@ -721,4 +751,10 @@ List of events for the model *nosv* with identifier **`V`** at version `2.1.0`:
|
||||
<dd>begins execution as delegate</dd>
|
||||
<dt><a id="VHD" href="#VHD"><pre>VHD</pre></a></dt>
|
||||
<dd>ceases execution as delegate</dd>
|
||||
<dt><a id="VPp" href="#VPp"><pre>VPp</pre></a></dt>
|
||||
<dd>sets progress state to Progressing</dd>
|
||||
<dt><a id="VPr" href="#VPr"><pre>VPr</pre></a></dt>
|
||||
<dd>sets progress state to Resting</dd>
|
||||
<dt><a id="VPa" href="#VPa"><pre>VPa</pre></a></dt>
|
||||
<dd>sets progress state to Absorbing</dd>
|
||||
</dl>
|
||||
|
BIN
doc/user/emulation/fig/breakdown-nosv.code_legend.png
Normal file
BIN
doc/user/emulation/fig/breakdown-nosv.code_legend.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 8.5 KiB |
BIN
doc/user/emulation/fig/breakdown-nosv.png
Normal file
BIN
doc/user/emulation/fig/breakdown-nosv.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 18 KiB |
@ -47,7 +47,7 @@ the following elements:
|
||||
|
||||
- A single byte model identification (for example `O`).
|
||||
- A set of runtime events with that model identification (see the [list
|
||||
of events](events)).
|
||||
of events](events.md)).
|
||||
- Rules that determine which sequences of events are valid.
|
||||
- The emulation hooks that process each event and modify the state of
|
||||
the emulator.
|
||||
|
@ -60,4 +60,47 @@ For more details, see [this MR][1].
|
||||
|
||||
The subsystem view provides a simplified view on what is the nOS-V
|
||||
runtime doing over time. The view follows the same rules described in
|
||||
the [subsystem view of Nanos6](../nanos6/#subsystem_view).
|
||||
the [subsystem view of Nanos6](nanos6.md/#subsystem_view).
|
||||
|
||||
|
||||
## Idle view
|
||||
|
||||
The idle view shows the progress state of the running threads:
|
||||
*Progressing* and *Resting*. The *Progressing* state is shown when they
|
||||
are making useful progress and the *Resting* state when they are waiting
|
||||
for work. When workers start running, by definition, they begin in the
|
||||
Progressing state and there are some situations that make them
|
||||
transition to Resting:
|
||||
|
||||
- When workers are waiting in the delegation lock after some spins or
|
||||
when instructed to go to sleep.
|
||||
- When the server is trying to serve tasks, but there are no more tasks
|
||||
available.
|
||||
|
||||
They will go back to Progressing as soon as they receive work. The
|
||||
specific points at which they do so can be read in [nOS-V source
|
||||
code](https://gitlab.bsc.es/nos-v/nos-v) by looking at the
|
||||
`instr_worker_resting()` and `instr_worker_progressing()` trace points.
|
||||
|
||||
This view is intended to detect parts of the execution time on which the
|
||||
workers don't have work, typically because the application doesn't have
|
||||
enough parallelism or the scheduler is unable to serve work fast enough.
|
||||
|
||||
## Breakdown view
|
||||
|
||||
The breakdown view displays a summary of what is happening in all CPUs
|
||||
by mixing in a single timeline the subsystem, idle and task type views.
|
||||
Specifically, it shows how many CPUs are resting as defined by the idle
|
||||
view, how many are inside a given task by showing the task type label,
|
||||
and how many are in a particular subsystem of the runtime.
|
||||
|
||||
!!! Important
|
||||
|
||||
You must specify *ovni.level = 3* or higher in *nosv.toml* and pass
|
||||
the *-b* option to ovniemu to generate the breakdown view.
|
||||
|
||||
Notice that the vertical axis shows the **number**
|
||||
of CPUs in that state, not the physical CPUs like other views.
|
||||
Here is an example of the Heat mini-app:
|
||||
|
||||

|
||||
|
@ -8,21 +8,20 @@ refer to the
|
||||
|
||||
The [LLVM OpenMP Runtime](https://openmp.llvm.org/design/Runtimes.html) provides
|
||||
an implementation of the OpenMP specification as a component of the LLVM
|
||||
compiler infrastructure. We have modified the LLVM OpenMP runtime to run on top
|
||||
compiler infrastructure. We have modified the LLVM OpenMP runtime (libomp) to run on top
|
||||
of the [nOS-V](https://gitlab.bsc.es/nos-v/nos-v) runtime as part of the
|
||||
[OmpSs-2 LLVM compiler](https://pm.bsc.es/llvm-ompss), named **OpenMP-V**.
|
||||
[OmpSs-2 LLVM compiler](https://pm.bsc.es/llvm-ompss), named **libompv**.
|
||||
|
||||
We have added instrumentation events to OpenMP-V designed to be enabled along
|
||||
We have added instrumentation events to libompv designed to be enabled along
|
||||
the [nOS-V instrumentation](nosv.md). This document describes all the
|
||||
instrumentation features included in our modified OpenMP-V runtime to identify
|
||||
instrumentation features included in our modified libompv runtime to identify
|
||||
what is happening. This data is useful for both users and developers of the
|
||||
OpenMP runtime to analyze issues and undesired behaviors.
|
||||
|
||||
!!! Note
|
||||
|
||||
Instrumenting the original OpenMP runtime from the LLVM project is planned
|
||||
but is not yet posible. For now you must use the modified OpenMP-V runtime
|
||||
with nOS-V.
|
||||
Instrumenting libomp is planned but is not yet posible.
|
||||
For now you must use libompv.
|
||||
|
||||
## Enable the instrumentation
|
||||
|
||||
@ -33,25 +32,25 @@ To generate runtime traces, you will have to:
|
||||
documentation](https://github.com/bsc-pm/nos-v/blob/master/docs/user/tracing.md).
|
||||
Typically you should use the `--with-ovni` option at configure time to specify
|
||||
where ovni is installed.
|
||||
2. **Build OpenMP-V with ovni and nOS-V support:** Use the `PKG_CONFIG_PATH`
|
||||
2. **Build libompv with ovni and nOS-V support:** Use the `PKG_CONFIG_PATH`
|
||||
environment variable to specify the nOS-V and ovni installation
|
||||
when configuring CMake.
|
||||
3. **Enable the instrumentation in nOS-V at runtime:** Refer to the
|
||||
[nOS-V documentation](https://github.com/bsc-pm/nos-v/blob/master/docs/user/tracing.md)
|
||||
to find out how to enable the tracing at runtime. Typically you can just set
|
||||
`NOSV_CONFIG_OVERRIDE="instrumentation.version=ovni"`.
|
||||
4. **Enable the instrumentation of OpenMP-V at runtime:** Set the environment
|
||||
4. **Enable the instrumentation of libompv at runtime:** Set the environment
|
||||
variable `OMP_OVNI=1`.
|
||||
|
||||
Currently there is only support for the subsystem view, which is documented
|
||||
below. The view is complemented with the information of [nOS-V views](nosv.md),
|
||||
as OpenMP-V uses nOS-V tasks to run the workers.
|
||||
Next sections describe each of the views included for analysis.
|
||||
|
||||
## Subsystem view
|
||||
|
||||

|
||||
|
||||
This view illustrates the activities of each thread with different states:
|
||||
The view is complemented with the information of [nOS-V views](nosv.md),
|
||||
as libompv uses nOS-V tasks to run the workers.
|
||||
Subsystem illustrates the activities of each thread with different states:
|
||||
|
||||
- **Work-distribution subsystem**: Related to work-distribution constructs,
|
||||
[in Chapter 11][workdis].
|
||||
@ -135,9 +134,9 @@ This view illustrates the activities of each thread with different states:
|
||||
- **Fork call**: Preparing a parallel section using the fork-join model.
|
||||
Only called from the master thread.
|
||||
|
||||
- **Init**: Initializing the OpenMP-V runtime.
|
||||
- **Init**: Initializing the libompv runtime.
|
||||
|
||||
- **Internal microtask**: Running a internal OpenMP-V function as a microtask.
|
||||
- **Internal microtask**: Running a internal libompv function as a microtask.
|
||||
|
||||
- **User microtask**: Running user code as a microtask in a worker thread.
|
||||
|
||||
@ -156,9 +155,31 @@ This view illustrates the activities of each thread with different states:
|
||||
[critical]: https://www.openmp.org/wp-content/uploads/OpenMP-API-Specification-5-2.pdf#section.15.2
|
||||
[barrier]: https://www.openmp.org/wp-content/uploads/OpenMP-API-Specification-5-2.pdf#section.15.3
|
||||
|
||||
## Label view
|
||||
|
||||
The label view displays the text in the `label()` clause of OpenMP
|
||||
tasks and work distribution constructs (static and dynamic for, single
|
||||
and section). When the label is not provided, the source file and source
|
||||
line location is used instead.
|
||||
|
||||
When nesting multiple tasks or work distribution constructs, only the
|
||||
innermost label is shown.
|
||||
|
||||
Note that in this view, the numeric event value is a hash function of
|
||||
the type label, so two distinct tasks (declared in different parts of
|
||||
the code) with the same label will share the event value and have the
|
||||
same color.
|
||||
|
||||
## Task ID view
|
||||
|
||||
The task ID view represents the numeric ID of the OpenMP task that is
|
||||
currently running on each thread. The ID is a monotonically increasing
|
||||
identifier assigned on task creation. Lower IDs correspond to tasks
|
||||
created at an earlier point than higher IDs.
|
||||
|
||||
## Limitations
|
||||
|
||||
As the compiler generates the code that perform the calls to the OpenMP-V
|
||||
As the compiler generates the code that perform the calls to the libompv
|
||||
runtime, there are some parts of the execution that are complicated to
|
||||
instrument by just placing a pair of events to delimite a function.
|
||||
|
||||
|
@ -60,4 +60,4 @@ will set all the channels to an error state.
|
||||
The emulator automatically switches the channels from one thread to
|
||||
another when a thread is switched from the CPU. So the different models
|
||||
don't need to worry about thread transitions. See the
|
||||
[channels](../channels) section for more information.
|
||||
[channels](../../dev/channels.md) section for more information.
|
||||
|
@ -27,10 +27,14 @@ Track changes in emulator model versions.
|
||||
|
||||
## Ovni
|
||||
|
||||
- ovni 1.1.0
|
||||
- Add support for mark events `OM[`, `OM]` and `OM=`
|
||||
- ovni 1.0.0: Initial version
|
||||
|
||||
## OpenMP
|
||||
|
||||
- openmp 1.2.0:
|
||||
- Add support for labels and task ID views
|
||||
- openmp 1.1.0: Initial version
|
||||
|
||||
## TAMPI
|
||||
@ -39,9 +43,15 @@ Track changes in emulator model versions.
|
||||
|
||||
## nOS-V
|
||||
|
||||
- nosv 2.4.0
|
||||
- Add support for `nosv_cond_wait`, `nosv_cond_signal` and `nosv_cond_broadcast` events VA{oOgGkK}.
|
||||
- nosv 2.3.0
|
||||
- Add `nosv.can_breakdown` attribute to metadata for breakdown checks.
|
||||
- nosv 2.2.0
|
||||
- Add support for progress events `VP{pra}`.
|
||||
- nosv 2.1.0
|
||||
- Add support for `nosv_mutex_lock`, `nosv_mutex_trylock` and `nosv_mutex_unlock` events VA{lLtTuU}.
|
||||
- Add support for `nosv_barrier_wait` event VA{bB}.
|
||||
- Add support for `nosv_mutex_lock`, `nosv_mutex_trylock` and `nosv_mutex_unlock` events `VA{lLtTuU}`.
|
||||
- Add support for `nosv_barrier_wait` event `VA{bB}`.
|
||||
- nosv 2.0.0
|
||||
- Add support for parallel tasks, adding a new `bodyid` argument in `VT*` events.
|
||||
- Remove support for old attach events `VH{aA}`.
|
||||
|
56
doc/user/runtime/env.md
Normal file
56
doc/user/runtime/env.md
Normal file
@ -0,0 +1,56 @@
|
||||
# Environment variables
|
||||
|
||||
Some environment variables can be used to adjust settings during the execution
|
||||
of libovni, they all begin with the `OVNI_` prefix. Be sure that all threads of
|
||||
the same node use the same environment variables.
|
||||
|
||||
## OVNI_TMPDIR
|
||||
|
||||
During the execution of your program, a per-thread buffer is kept where the new
|
||||
events are being recorded. When this buffer is full, it is written to disk and
|
||||
emptied, an operation known as flush. This may take a while depending on the
|
||||
underlying filesystem.
|
||||
|
||||
Keep in mind that the thread will be blocked until the flush ends, so if your
|
||||
filesystem is slow it would interrupt the execution of your program for a long
|
||||
time. It is advisable to use the fastest filesystem available (see the tmpfs(5)
|
||||
and df(1) manual pages).
|
||||
|
||||
You can select a temporary trace directory where the buffers will be flushed
|
||||
during the execution by setting the environment variable `OVNI_TMPDIR`. The last
|
||||
directory will be created if doesn't exist. In that case, as soon as a process
|
||||
calls `ovni_proc_fini()`, the traces of all its threads will be moved to the
|
||||
final directory at `$PWD/ovni`. Example:
|
||||
|
||||
OVNI_TMPDIR=$(mktemp -u /dev/shm/ovni.XXXXXX) srun ./your-app
|
||||
|
||||
To test the different filesystem speeds, you can use hyperfine and dd. Take a
|
||||
closer look at the max time:
|
||||
|
||||
```
|
||||
$ hyperfine 'dd if=/dev/zero of=/gpfs/projects/bsc15/bsc15557/kk bs=2M count=10'
|
||||
Benchmark 1: dd if=/dev/zero of=/gpfs/projects/bsc15/bsc15557/kk bs=2M count=10
|
||||
Time (mean ± σ): 71.7 ms ± 130.4 ms [User: 0.8 ms, System: 10.2 ms]
|
||||
Range (min … max): 14.7 ms … 1113.2 ms 162 runs
|
||||
|
||||
Warning: Statistical outliers were detected. Consider re-running this
|
||||
benchmark on a quiet PC without any interferences from other programs. It
|
||||
might help to use the '--warmup' or '--prepare' options.
|
||||
|
||||
$ hyperfine 'dd if=/dev/zero of=/tmp/kk bs=2M count=10'
|
||||
Benchmark 1: dd if=/dev/zero of=/tmp/kk bs=2M count=10
|
||||
Time (mean ± σ): 56.2 ms ± 5.7 ms [User: 0.6 ms, System: 14.8 ms]
|
||||
Range (min … max): 45.8 ms … 77.8 ms 63 runs
|
||||
|
||||
$ hyperfine 'dd if=/dev/zero of=/dev/shm/kk bs=2M count=10'
|
||||
Benchmark 1: dd if=/dev/zero of=/dev/shm/kk bs=2M count=10
|
||||
Time (mean ± σ): 11.4 ms ± 0.4 ms [User: 0.5 ms, System: 11.1 ms]
|
||||
Range (min … max): 9.7 ms … 12.5 ms 269 runs
|
||||
```
|
||||
|
||||
## OVNI_TRACEDIR
|
||||
|
||||
By default, the runtime trace will be placed in the `ovni` directory, inside the
|
||||
working directory. You can specify a different location to place the trace by
|
||||
setting the `OVNI_TRACEDIR` environment variable. It accepts a relative or
|
||||
absolute path, which will be created if it doesn't exist.
|
BIN
doc/user/runtime/fig/mark.png
Normal file
BIN
doc/user/runtime/fig/mark.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 6.6 KiB |
123
doc/user/runtime/index.md
Normal file
123
doc/user/runtime/index.md
Normal file
@ -0,0 +1,123 @@
|
||||
# Introduction
|
||||
|
||||
To use *libovni* to instrument a program follow the next instructions
|
||||
carefully or you may end up with an incomplete trace that is rejected at
|
||||
emulation.
|
||||
|
||||
You can also generate a valid trace from your own software or hardware
|
||||
directly following the [trace specification](trace_spec.md).
|
||||
|
||||
## Initialization
|
||||
|
||||
To initialize libovni follow these steps in all threads:
|
||||
|
||||
1. **Check the version**. Call `ovni_version_check()` once before calling any
|
||||
ovni function. It can be called multiple times from any thread, but only one
|
||||
is required.
|
||||
|
||||
2. **Init the process**. Call `ovni_proc_init()` to initialize the process. It
|
||||
can only be called **once per process** and it must be called before the
|
||||
thread is initialized.
|
||||
|
||||
3. **Init the thread**. Call `ovni_thread_init()` to initialize the thread.
|
||||
Multiple attempts to initialize the same thread are ignored with a warning.
|
||||
Must be called by all threads.
|
||||
|
||||
The `ovni_proc_init()` arguments are as follows:
|
||||
|
||||
```c
|
||||
void ovni_proc_init(int app, const char *loom, int pid);
|
||||
```
|
||||
|
||||
The `app` defines the "appid" of the program, which must be a number >0. This is
|
||||
useful to run multiple processes some of which run the same "app", so you can
|
||||
tell which one is which. The `loom` argument defines the
|
||||
[loom](../concepts/part-model.md#loom) name and maps the process to that
|
||||
loom. It must be composed of the host name, a dot and a suffix. The PID is the
|
||||
one obtained by `getpid(2)`.
|
||||
|
||||
The `ovni_thread_init()` function only accepts one argument, the TID as returned
|
||||
by `gettid(2)`.
|
||||
|
||||
## Setup metadata
|
||||
|
||||
Once the process and thread are initialized, you can begin adding metadata to
|
||||
the thread stream.
|
||||
|
||||
1. **Require models**. Call `ovni_thread_require()` with the required model
|
||||
version before emitting events for a given model. Only required once from a
|
||||
thread in a given trace. The `ovni` model is implicitly required when calling
|
||||
`ovni_thread_init()`, so there is no need to add it again.
|
||||
|
||||
2. **Emit loom CPUs**. Call `ovni_add_cpu()` to register each CPU in the loom. It can
|
||||
be done from a single thread or multiple threads, in the latter the list of
|
||||
CPUs is merged.
|
||||
|
||||
3. **Set the rank**. If you use MPI, call `ovni_proc_set_rank()` to register the
|
||||
rank and number of ranks of the current execution. Only once per process.
|
||||
|
||||
When emitting the CPUs with:
|
||||
|
||||
```c
|
||||
void ovni_add_cpu(int index, int phyid);
|
||||
```
|
||||
|
||||
The `index` will be used to identify the CPU in the loom and goes from 0 to N -
|
||||
1, where N is the number of CPUs in the loom. It must match the index that is
|
||||
used in affinity events when a thread switches to another CPU. The `phyid` is
|
||||
only displayed in Paraver and is usually the same as the index, but it can be
|
||||
different if there are multiple looms per node.
|
||||
|
||||
## Start the execution
|
||||
|
||||
The current thread must switch to the "Running" state before any event can be
|
||||
processed by the emulator. Do so by emitting a [`OHx`
|
||||
event](../emulation/events.md#OHx) in the stream with the appropriate payload:
|
||||
|
||||
```c
|
||||
static void thread_execute(int32_t cpu, int32_t ctid, uint64_t tag)
|
||||
{
|
||||
struct ovni_ev ev = {0};
|
||||
ovni_ev_set_clock(&ev, ovni_clock_now());
|
||||
ovni_ev_set_mcv(&ev, "OHx");
|
||||
ovni_payload_add(&ev, (uint8_t *) &cpu, sizeof(cpu));
|
||||
ovni_payload_add(&ev, (uint8_t *) &ctid, sizeof(ctid));
|
||||
ovni_payload_add(&ev, (uint8_t *) &tag, sizeof(tag));
|
||||
ovni_ev_emit(&ev);
|
||||
}
|
||||
```
|
||||
|
||||
The `cpu` is the logical index (not the physical ID) of the loom CPU at which
|
||||
this thread will begin the execution. Use -1 if it is not known. The `ctid` and
|
||||
`tag` allow you to track the exact point at which a given thread was created and
|
||||
by which thread but they are not relevant for the first thread, so they can be
|
||||
set to -1.
|
||||
|
||||
## Emit events
|
||||
|
||||
After this point you can emit any other event from this thread. Use the
|
||||
`ovni_ev_*` set of functions to create and emit events. Notice that all events
|
||||
refer to the current thread that emits them.
|
||||
|
||||
If you need to store metadata information, use the `ovni_attr_*` set of
|
||||
functions. The metadata is stored in disk by `ovni_attr_flush()` and when the
|
||||
thread is freed by `ovni_thread_free()`.
|
||||
|
||||
Attempting to emit events or writing metadata without having a thread
|
||||
initialized will cause your program to abort.
|
||||
|
||||
## Finishing the execution
|
||||
|
||||
To finalize the execution **every thread** must perform the following steps,
|
||||
otherwise the trace **will be rejected**.
|
||||
|
||||
1. **End the current thread**. Emit a [`OHe` event](../emulation/events.md#OHe) to inform the current thread ends.
|
||||
2. **Flush the buffer**. Call `ovni_flush()` to be sure all events are written
|
||||
to disk.
|
||||
3. **Free the thread**. Call `ovni_thread_free()` to complete the stream and
|
||||
free the memory used by the buffer.
|
||||
4. **Finish the process**. If this is the last thread, call `ovni_proc_fini()`
|
||||
to set the process state to finished.
|
||||
|
||||
If a thread fails to perform these steps, the complete trace will be rejected by
|
||||
the emulator as it cannot guarantee it to be consistent.
|
218
doc/user/runtime/mark.md
Normal file
218
doc/user/runtime/mark.md
Normal file
@ -0,0 +1,218 @@
|
||||
# Mark events
|
||||
|
||||
The mark API allows you to add arbitrary events in a trace to mark regions of
|
||||
interest while debugging or developing a new program or library. The events are
|
||||
processed by the emulator to generate a timeline.
|
||||
|
||||
## Usage in runtime
|
||||
|
||||
Follow these steps to correctly use the API. Most problems will be detected in
|
||||
emulation and cause a panic if you are not careful.
|
||||
|
||||
### Create a mark type
|
||||
|
||||
You can create up to 100 types of marks, each will be shown in its own Paraver
|
||||
timeline. To create a new type, use the following call:
|
||||
|
||||
```c
|
||||
void ovni_mark_type(int32_t type, long flags, const char *title);
|
||||
```
|
||||
|
||||
The numeric value `type` must be a number between 0 and 99 (both included). The
|
||||
`title` will be used to give a name to the Paraver timeline.
|
||||
|
||||
The default with flags set to zero, is to create a channel that can hold a
|
||||
single value only (see [channels](../../dev/channels.md)). To use a stack of
|
||||
values add the `OVNI_MARK_STACK` value to the flags.
|
||||
|
||||
Only one thread among all nodes needs to define a type to be available globally,
|
||||
but the same type can be defined from multiple threads, as long as the same
|
||||
flags and title argument are used. The idea is to avoid having to check if the
|
||||
type was already defined or not.
|
||||
|
||||
### Define labels (optional)
|
||||
|
||||
The values that are written to the channel can have labels to display in the
|
||||
Paraver timeline. The labels are optional, if not given the numeric value will
|
||||
be shown in Paraver.
|
||||
|
||||
Use the following call to register a label for a value in a given type.
|
||||
|
||||
```c
|
||||
void ovni_mark_label(int32_t type, int64_t value, const char *label);
|
||||
```
|
||||
|
||||
A value can only have at most a single label associated. Multiple threads can
|
||||
call the `ovni_mark_label()` with the same type and value as long as they use
|
||||
the same label. New labels for the same type can be associated from different
|
||||
threads, as long as the values are different.
|
||||
|
||||
All value and label pairs are combined from all threads and will be available in
|
||||
the Paraver view for each type.
|
||||
|
||||
### Emit events
|
||||
|
||||
All mark channels begin with the default value *null*, which is not shown in
|
||||
Paraver and will be displayed as the usual empty space. The value of the channel
|
||||
can be changed over time with the following functions.
|
||||
|
||||
!!! warning
|
||||
|
||||
The value 0 is forbidden, as it is used by Paraver to represent the
|
||||
"empty" state.
|
||||
|
||||
If you have used a single channel (without `OVNI_MARK_STACK`), then you must use
|
||||
the following call to emit events at runtime:
|
||||
|
||||
```c
|
||||
void ovni_mark_set(int32_t type, int64_t value);
|
||||
```
|
||||
|
||||
It will update the value of the channel to the given `value`.
|
||||
|
||||
If you have used a stack channel (with `OVNI_MARK_STACK`), then you must use the
|
||||
push/pop set of calls:
|
||||
|
||||
```c
|
||||
void ovni_mark_push(int32_t type, int64_t value);
|
||||
void ovni_mark_pop(int32_t type, int64_t value);
|
||||
```
|
||||
|
||||
The value in the pop call must match the previous pushed value.
|
||||
|
||||
<details>
|
||||
<summary>Example OmpSs-2 program</summary>
|
||||
<br>
|
||||
<div style="padding-left: 1em; border-left: 3px solid #ddd">
|
||||
<p>
|
||||
Here is a dummy program showing how to use the mark API with an OmpSs-2 program.
|
||||
Notice that there is no initialization of the current thread or process, as it
|
||||
already occurs inside the OmpSs-2 runtime before reaching the main.
|
||||
|
||||
```c
|
||||
/* Build with:
|
||||
* $ clang -fompss-2 -lovni dummy.c -o dummy
|
||||
* Enable instrumentation in nanos6:
|
||||
* $ echo 'version.instrument = "ovni"' > nanos6.toml
|
||||
* Run:
|
||||
* $ ./dummy
|
||||
* Emulate:
|
||||
* $ ovniemu ovni
|
||||
* View timeline:
|
||||
* $ wxparaver ovni/cpu.prv ovni/cfg/cpu/ovni/mark.cfg
|
||||
*/
|
||||
#include <ovni.h>
|
||||
#include <unistd.h>
|
||||
|
||||
enum { INDEX = 0, RUN = 1 };
|
||||
|
||||
static void process(int run, int i)
|
||||
{
|
||||
ovni_mark_push(RUN, run + 1);
|
||||
ovni_mark_push(INDEX, i + 1);
|
||||
usleep(10000); // Dummy operation for 10 ms
|
||||
ovni_mark_pop(INDEX, i + 1);
|
||||
ovni_mark_pop(RUN, run + 1);
|
||||
}
|
||||
|
||||
int main(void)
|
||||
{
|
||||
ovni_mark_type(INDEX, OVNI_MARK_STACK, "Index");
|
||||
ovni_mark_type(RUN, OVNI_MARK_STACK, "Run");
|
||||
|
||||
for (int run = 0; run < 10; run++) {
|
||||
for (int i = 0; i < 50; i++) {
|
||||
#pragma oss task
|
||||
process(run, i);
|
||||
}
|
||||
}
|
||||
|
||||
#pragma oss taskwait
|
||||
|
||||
return 0;
|
||||
}
|
||||
```
|
||||
|
||||
<!-- Images don't seem to work via markdown -->
|
||||
<p>Here is the resulting timeline loaded in Paraver with the gradient color
|
||||
configuration, showing the first mark type (the index):
|
||||
<img style="margin-top: 1em" alt="" src="../fig/mark.png"></p>
|
||||
|
||||
</div>
|
||||
</details>
|
||||
<p></p>
|
||||
|
||||
## Usage in Paraver
|
||||
|
||||
Each thread holds a channel for each mark type that you have defined. The
|
||||
information of the mark channels is propagated to the Paraver timeline in
|
||||
Thread and CPU views.
|
||||
|
||||
When a thread is not *running*, the value of the mark channels is not shown in
|
||||
Paraver. In the case of the CPU timeline, only the values of the running thread are
|
||||
shown. If there are no running threads, nothing is shown.
|
||||
|
||||
Follow the next steps to create a configuration to suit your needs. You only
|
||||
need to do it once, then you can save the configuration file and reuse it for
|
||||
future traces.
|
||||
|
||||
### Filtering the type
|
||||
|
||||
To see a mark type, you will have to create a Paraver configuration that matches
|
||||
the type that you have created. The mark `type` value gets converted into a PRV
|
||||
type by adding 100 (as values from 0 to 99 are reserved).
|
||||
|
||||
You can use the `cpu/ovni/mark.cfg` and `thread/ovni/mark.cfg` configurations as
|
||||
a starting point to create your own.
|
||||
|
||||
Go to "Window Properties" (the second button under "Files & Window Properties")
|
||||
and then go to Filter > Events > Event type. Set Function to `=` and click the
|
||||
Types value to display the `[...]` button, which will allow you to choose which
|
||||
type to display.
|
||||
|
||||
In the "Events Selection" window, ensure that only one type is selected, and the
|
||||
"Values" panel shows Function "All", to see all values for the selected type.
|
||||
|
||||
### Setting the title
|
||||
|
||||
In the "Window Properties" adjust the Name so it reflects what you are seeing.
|
||||
This will be shown in the saved images, so it is good to use a proper
|
||||
description.
|
||||
|
||||
### Configure the display method
|
||||
|
||||
By default, the timeline will display the values as "Code color". To switch to a
|
||||
gradient or other methods, left-click in the timeline and go to "Paint As" and
|
||||
select "Gradient" (or others).
|
||||
|
||||
You may also want to adjust the "Drawmode" which determines what happens when
|
||||
there are multiple values under a given pixel. This is specially important when
|
||||
you are viewing the trace with a large time range, before zooming into a given
|
||||
region.
|
||||
|
||||
By default, the "Random not zero" mode is selected, which will select a
|
||||
random value from the ones under each pixel, disregarding the occurrences of each
|
||||
value. This mode will give importance to rare values, so it is usually a safe
|
||||
starting point. The "Last" mode will show the last value in that pixel, which is
|
||||
more or less fair, but will often hide rare values.
|
||||
|
||||
To change in both horizontal (Time) and in vertical (Objects) directions, go to:
|
||||
left click on timeline > Drawmode > Both > Last.
|
||||
|
||||
### Ensure the range is good
|
||||
|
||||
Paraver will only display values in the timeline that are in the Semantic
|
||||
range. If you see a red triangle in the lower left corner then there are values
|
||||
outside the range that are not being displayed. You can click on this button to
|
||||
expand the range to cover all values in the current view.
|
||||
|
||||
The opposite may also happen, where the range is too big for the current values.
|
||||
You can also click on the same spot (even if the triangle is not shown) to
|
||||
shrink the range to cover the values in the view, or go to the Window Properties
|
||||
and modify the "Semantic Minimum" and "Semantic Maximum" values manually.
|
||||
|
||||
### Save the configuration
|
||||
|
||||
Once you finish configuring the timeline, save the configuration by
|
||||
left-clicking the view and then "Save > Configuration...". You can use this
|
||||
configuration in future traces to avoid doing these steps again.
|
@ -1,127 +1,149 @@
|
||||
# Trace specification
|
||||
# Trace specification v3
|
||||
|
||||
!!! Important
|
||||
|
||||
This document refers to the trace specification for
|
||||
the version 2
|
||||
the version 3
|
||||
|
||||
The ovni instrumentation library stores the information collected in a
|
||||
trace following the specification of this document.
|
||||
The ovni instrumentation library libovni stores the information
|
||||
collected in a runtime trace following the specification of this document.
|
||||
|
||||
## Structure
|
||||
|
||||
An ovni runtime trace (or simply, a trace) is composed of one or more
|
||||
[streams](../concepts/trace.md#stream), which are directories containing
|
||||
two mandatory files:
|
||||
|
||||
- `stream.json` the stream metadata in JSON format.
|
||||
- `stream.obs` the binary stream with events.
|
||||
|
||||
Each stream is assigned to a single *part* in the [part
|
||||
model](../concepts/part-model.md), usually assigned to a given thread.
|
||||
|
||||
There are no imposed rules on how to organize the several streams into
|
||||
directories, but libovni uses the following approach for thread streams:
|
||||
|
||||
The complete trace is stored in a top-level directory named `ovni`.
|
||||
Inside this directory you will find the loom directories with the prefix
|
||||
`loom.`. The name of the loom is built from the `loom` parameter of
|
||||
`ovni_proc_init()`, prefixing it with `loom.`.
|
||||
Inside this directory you will find the loom directories. The name of
|
||||
the loom directory is built from the `loom` parameter of `ovni_proc_init()`,
|
||||
prefixing it with `loom.`.
|
||||
|
||||
Each loom directory contains one directory per process of that loom. The
|
||||
name is composed of the `proc.` prefix and the PID of the process
|
||||
specified in the `pid` argument to `ovni_proc_init()`.
|
||||
|
||||
Each process directory contains:
|
||||
Inside each process there is one directory for each thread, composed by
|
||||
the `thread.` prefix and the TID, which are the streams. The files
|
||||
`stream.json` and `stream.obs` reside inside. Example:
|
||||
|
||||
- The process metadata file `metadata.json`.
|
||||
- The thread streams, composed of:
|
||||
- The binary stream like `thread.123.obs`
|
||||
- The thread metadata like `thread.123.json`
|
||||
```
|
||||
ovni/loom.mio.nosv-u1000/proc.89719/thread.89719/stream.json
|
||||
ovni/loom.mio.nosv-u1000/proc.89719/thread.89719/stream.obs
|
||||
```
|
||||
|
||||
## Process metadata
|
||||
This structure prevents collisions among threads with the same TID among nodes,
|
||||
while allowing dumping events from a single thread, process or loom with
|
||||
ovnidump.
|
||||
|
||||
!!! Important
|
||||
## Stream metadata
|
||||
|
||||
Process metadata has version 2
|
||||
The `stream.json` metadata file contains information about the part that
|
||||
the stream is assigned to. This is generally used to determine the
|
||||
hierarchy of the part model.
|
||||
|
||||
The process metadata file contains important information about the trace
|
||||
that is invariant during the complete execution, and generally is
|
||||
required to be available prior to processing the events in the trace.
|
||||
|
||||
The metadata is stored in the JSON file `metadata.json` inside each
|
||||
process directory and contains the following keys:
|
||||
The JSON must be an object (dictionary) with the following mandatory
|
||||
keys:
|
||||
|
||||
- `version`: a number specifying the version of the metadata format.
|
||||
Must have the value 2 for this version.
|
||||
- `app_id`: the application ID, used to distinguish between applications
|
||||
running on the same loom.
|
||||
- `rank`: the rank of the MPI process (optional).
|
||||
- `nranks`: number of total MPI processes (optional).
|
||||
- `cpus`: the array of $`N_c`$ CPUs available in the loom. Only one
|
||||
process in the loom must contain this mandatory key. Each element is a
|
||||
dictionary with the keys:
|
||||
- `index`: containing the logical CPU index from 0 to $`N_c - 1`$.
|
||||
- `phyid`: the number of the CPU as given by the operating system
|
||||
(which can exceed $`N_c`$).
|
||||
Must have the value 3 for this version.
|
||||
|
||||
Here is an example of the `metadata.json` file:
|
||||
The rest of information is stored for each model.
|
||||
|
||||
```
|
||||
{
|
||||
"version": 2,
|
||||
"app_id": 1,
|
||||
"rank": 0,
|
||||
"nranks": 4,
|
||||
"cpus": [
|
||||
{
|
||||
"index": 0,
|
||||
"phyid": 0
|
||||
},
|
||||
{
|
||||
"index": 1,
|
||||
"phyid": 1
|
||||
},
|
||||
{
|
||||
"index": 2,
|
||||
"phyid": 2
|
||||
},
|
||||
{
|
||||
"index": 3,
|
||||
"phyid": 3
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
In particular, the `ovni` model enforces the use of:
|
||||
|
||||
## Thread metadata
|
||||
- `ovni.part`: the type of part this stream is assigned to, usually
|
||||
`thread`.
|
||||
- `ovni.require`: a dictionary of model name and version which will
|
||||
determine which models are enabled at emulation and the required
|
||||
version.
|
||||
- `ovni.finished`: must be 1 to ensure the stream is complete (mandatory
|
||||
in all streams).
|
||||
|
||||
!!! Important
|
||||
### Thread stream metadata
|
||||
|
||||
Thread metadata has version 2
|
||||
For `thread` streams, the following attributes are used.
|
||||
|
||||
The thread metadata stores constant information per thread, like the
|
||||
process metadata. The information is stored in a dictionary, where the
|
||||
name of the emulation models are used as keys. In particular, the
|
||||
libovni library writes information in the "ovni" key, such as the
|
||||
model requirements, and other information like the version of libovni
|
||||
used. Example:
|
||||
- `ovni.tid`: the TID of the thread (mandatory, per-thread).
|
||||
- `ovni.pid`: the PID of the process that the thread belongs to (mandatory, per-thread).
|
||||
- `ovni.app_id`: the application ID of the process (optional, per-process).
|
||||
- `ovni.rank`: the rank of the MPI process (optional, per-process).
|
||||
- `ovni.nranks`: number of total MPI processes (optional, per-process).
|
||||
- `ovni.loom`: the name of the loom that the process belongs to (mandatory, per-process).
|
||||
- `ovni.loom_cpus`: the array of N CPUs available in the loom
|
||||
(mandatory, per-loom). Each element is a dictionary with the keys:
|
||||
- `index`: containing the logical CPU index from 0 to N - 1.
|
||||
- `phyid`: the number of the CPU as given by the operating system
|
||||
(which can exceed N).
|
||||
|
||||
Notice that some attributes don't need to be present in all thread
|
||||
streams. For example, per-process requires that at least one thread
|
||||
contains the attribute for each process. Similarly, per-loom requires
|
||||
that at least one thread of the loom emits the attribute.
|
||||
|
||||
The final attribute value will be computed by merging all the values from the
|
||||
children metadata. Simple values like numbers or strings must match exactly if
|
||||
they appear duplicated, arrays are appended.
|
||||
|
||||
Other attributes can be used for other models.
|
||||
|
||||
Here is an example of the `stream.json` file for a thread of a nOS-V
|
||||
program:
|
||||
|
||||
```json
|
||||
{
|
||||
"version": 2,
|
||||
"version": 3,
|
||||
"ovni": {
|
||||
"lib": {
|
||||
"version": "1.4.0",
|
||||
"commit": "unknown"
|
||||
"version": "1.10.0",
|
||||
"commit": "dirty"
|
||||
},
|
||||
"part": "thread",
|
||||
"tid": 89719,
|
||||
"pid": 89719,
|
||||
"loom": "mio.nosv-u1000",
|
||||
"app_id": 1,
|
||||
"require": {
|
||||
"ovni": "1.0.0"
|
||||
}
|
||||
"ovni": "1.1.0",
|
||||
"nosv": "2.3.0"
|
||||
},
|
||||
"loom_cpus": [
|
||||
{ "index": 0, "phyid": 0 },
|
||||
{ "index": 1, "phyid": 1 },
|
||||
{ "index": 2, "phyid": 2 },
|
||||
{ "index": 3, "phyid": 3 }
|
||||
],
|
||||
"finished": 1
|
||||
},
|
||||
"nosv": {
|
||||
"can_breakdown": false,
|
||||
"lib_version": "2.3.1"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
The metadata is written to disk when the thread is first initialized
|
||||
and when the thread finishes.
|
||||
|
||||
## Thread binary streams
|
||||
## Binary stream
|
||||
|
||||
!!! Important
|
||||
|
||||
Thread binary stream has version 1
|
||||
Binary streams have version 1
|
||||
|
||||
Streams are a binary files that contains a succession of events with
|
||||
monotonically increasing clock values. Streams have a small header and
|
||||
the variable size events just after the header.
|
||||
A binary stream is a binary file named `stream.obs` that contains a
|
||||
succession of events with monotonically increasing clock values. They
|
||||
have a small header and the variable size events just after the header.
|
||||
|
||||
The header contains the magic 4 bytes of "ovni" and a version number of
|
||||
4 bytes too. Here is a figure of the data stored in disk:
|
||||
4 bytes too. Here is a figure of the data stored in disk on a little
|
||||
endian machine:
|
||||
|
||||

|
||||
|
||||
@ -145,7 +167,7 @@ payload:
|
||||
- Normal events: with a payload up to 16 bytes
|
||||
- Jumbo events: with a payload up to $`2^{32}`$ bytes
|
||||
|
||||
## Normal events
|
||||
### Normal events
|
||||
|
||||
The normal events are composed of:
|
||||
|
||||
@ -178,7 +200,7 @@ In the following figure you can see each field annotated:
|
||||
|
||||

|
||||
|
||||
## Jumbo events
|
||||
### Jumbo events
|
||||
|
||||
The jumbo events are just like normal events but they can hold large
|
||||
data. The size of the jumbo data is stored as a 32 bits integer as a
|
||||
@ -203,10 +225,10 @@ In the following figure you can see each field annotated:
|
||||
|
||||

|
||||
|
||||
## Design considerations
|
||||
### Design considerations
|
||||
|
||||
The stream format has been designed to be very simple, so writing a
|
||||
parser library would take no more than 2 days for a single developer.
|
||||
The binary stream format has been designed to be very simple, so writing
|
||||
a parser library would take no more than 2 days for a single developer.
|
||||
|
||||
The size of the events has been designed to be small, with 12 bytes per
|
||||
event when no payload is used.
|
||||
@ -239,11 +261,7 @@ raw stream in binary, as the MCV codes can be read as ASCII characters:
|
||||
This allows a human to detect signs of corruption by visually inspecting
|
||||
the streams.
|
||||
|
||||
## Limitations
|
||||
### Limitations
|
||||
|
||||
The streams are designed to be read only forward, as they only contain
|
||||
the size of each event in the header.
|
||||
|
||||
Currently, we only support using the threads as sources of events, using
|
||||
one stream per thread. However, adding support for more streams from
|
||||
multiple sources is planned for the future.
|
||||
|
@ -1,94 +0,0 @@
|
||||
# Tracing a new program
|
||||
|
||||
Read carefully this document before using libovni to instrument a new
|
||||
component. There are a few rules you must follow to ensure the runtime
|
||||
trace is correct.
|
||||
|
||||
## Trace processes and threads
|
||||
|
||||
- Call `ovni_version_check()` once before calling any ovni function.
|
||||
|
||||
- Call `ovni_proc_init()` when a new process begins the execution.
|
||||
|
||||
- Call `ovni_thread_init()` when a new thread begins the execution
|
||||
(including the main process thread).
|
||||
|
||||
- Call `ovni_thread_require()` with the required model version before
|
||||
emitting events for that model.
|
||||
|
||||
- Call `ovni_flush()` and `ovni_thread_free()` when it finishes (in that
|
||||
order).
|
||||
|
||||
- Call `ovni_proc_fini()` when a process ends, after all threads have
|
||||
finished.
|
||||
|
||||
You can use `ovni_ev_emit()` to record a new event. If you need more
|
||||
than 16 bytes of payload, use `ovni_ev_jumbo_emit()`. See the [trace
|
||||
specification](../trace_spec) for more details.
|
||||
|
||||
Compile and link with libovni. When you run your program, a new
|
||||
directory ovni will be created in the current directory `$PWD/ovni`
|
||||
which contains the execution trace.
|
||||
|
||||
You can change the trace directory by defining the `OVNI_TRACEDIR`
|
||||
environment variable. The envar accepts a trace directory name, a
|
||||
relative path to the trace directory, or its absolute path. In the
|
||||
first case, the trace directory will be created in the current
|
||||
directory `$PWD`.
|
||||
|
||||
## Rules
|
||||
|
||||
Follow these rules to avoid losing events:
|
||||
|
||||
1. No event may be emitted until the process is initialized with
|
||||
`ovni_proc_init()` and the thread with `ovni_thread_init()`.
|
||||
|
||||
2. When a thread ends the execution, it must call `ovni_flush()` to write the
|
||||
events in the buffer to disk.
|
||||
|
||||
3. All threads must have flushed its buffers before calling `ovni_proc_fini()`.
|
||||
|
||||
## Select a fast directory
|
||||
|
||||
During the execution of your program, a per-thread buffer is kept where the new
|
||||
events are being recorded. When this buffer is full, it is written to disk and
|
||||
emptied, an operation known as flush. This may take a while depending on the
|
||||
underliying filesystem.
|
||||
|
||||
Keep in mind that the thread will be blocked until the flush ends, so if your
|
||||
filesystem is slow it would interrupt the execution of your program for a long
|
||||
time. It is advisable to use the fastest filesystem available (see the tmpfs(5)
|
||||
and df(1) manual pages).
|
||||
|
||||
You can select the trace directory where the buffers will be flushed during the
|
||||
execution by setting the environment variable `OVNI_TMPDIR`. The last directory
|
||||
will be created if doesn't exist. In that case, as soon as a process calls
|
||||
`ovni_proc_fini()`, the traces of all its threads will be moved to the final
|
||||
directory at `$PWD/ovni`. Example:
|
||||
|
||||
OVNI_TMPDIR=$(mktemp -u /dev/shm/ovni.XXXXXX) srun ./your-app
|
||||
|
||||
To test the different filesystem speeds, you can use hyperfine and dd. Take a
|
||||
closer look at the max time:
|
||||
|
||||
```
|
||||
$ hyperfine 'dd if=/dev/zero of=/gpfs/projects/bsc15/bsc15557/kk bs=2M count=10'
|
||||
Benchmark 1: dd if=/dev/zero of=/gpfs/projects/bsc15/bsc15557/kk bs=2M count=10
|
||||
Time (mean ± σ): 71.7 ms ± 130.4 ms [User: 0.8 ms, System: 10.2 ms]
|
||||
Range (min … max): 14.7 ms … 1113.2 ms 162 runs
|
||||
|
||||
Warning: Statistical outliers were detected. Consider re-running this
|
||||
benchmark on a quiet PC without any interferences from other programs. It
|
||||
might help to use the '--warmup' or '--prepare' options.
|
||||
|
||||
$ hyperfine 'dd if=/dev/zero of=/tmp/kk bs=2M count=10'
|
||||
Benchmark 1: dd if=/dev/zero of=/tmp/kk bs=2M count=10
|
||||
Time (mean ± σ): 56.2 ms ± 5.7 ms [User: 0.6 ms, System: 14.8 ms]
|
||||
Range (min … max): 45.8 ms … 77.8 ms 63 runs
|
||||
|
||||
$ hyperfine 'dd if=/dev/zero of=/dev/shm/kk bs=2M count=10'
|
||||
Benchmark 1: dd if=/dev/zero of=/dev/shm/kk bs=2M count=10
|
||||
Time (mean ± σ): 11.4 ms ± 0.4 ms [User: 0.5 ms, System: 11.1 ms]
|
||||
Range (min … max): 9.7 ms … 12.5 ms 269 runs
|
||||
```
|
||||
|
46
flake.nix
46
flake.nix
@ -15,29 +15,33 @@
|
||||
nosv = prev.nosv.override {
|
||||
useGit = true;
|
||||
gitBranch = "master";
|
||||
gitCommit = "c698c16c0518e6afc68fb32ee6f1a0f65ca69327";
|
||||
gitCommit = "3ca2f67993f85aa73c53f810ff12148189eae642";
|
||||
};
|
||||
nanos6 = prev.nanos6.override {
|
||||
useGit = true;
|
||||
gitBranch = "master";
|
||||
gitCommit = "21fccec383a4136daf5919093a6ffcdc8c139bfe";
|
||||
gitCommit = "f39ea57c67a613d098050e2bb251116a021e91e5";
|
||||
};
|
||||
nodes = prev.nodes.override {
|
||||
useGit = true;
|
||||
gitBranch = "master";
|
||||
gitCommit = "70ce0ed0a20842d8eb3124aa5db5916fb6fc238f";
|
||||
gitCommit = "c97d7ca6f885500121a94c75df429c788e8d6cf8";
|
||||
};
|
||||
clangOmpss2Unwrapped = prev.clangOmpss2Unwrapped.override {
|
||||
useGit = true;
|
||||
gitBranch = "master";
|
||||
gitCommit = "b813108e2810c235480688ed7d1b0f1faf76e804";
|
||||
gitCommit = "b7af30b36be3e7e90b33c5f01a3f7e3656df785f";
|
||||
};
|
||||
openmp = prev.openmp.overrideAttrs (old: {
|
||||
# Newer version of LLVM OpenMP requires python3
|
||||
nativeBuildInputs = (old.nativeBuildInputs or []) ++ [ final.python3 ];
|
||||
});
|
||||
|
||||
# Use a fixed commit for libovni
|
||||
ovniFixed = prev.ovni.override {
|
||||
useGit = true;
|
||||
gitBranch = "master";
|
||||
gitCommit = "68fc8b0eba299c3a7fa3833ace2c94933a26749e";
|
||||
gitCommit = "3bbfe0f0ecdf58e3f46ebafdf2540680f990b76b";
|
||||
};
|
||||
# Build with the current source
|
||||
ovniLocal = prev.ovni.overrideAttrs (old: rec {
|
||||
@ -134,6 +138,38 @@
|
||||
export ASAN_OPTIONS=detect_leaks=0
|
||||
'';
|
||||
});
|
||||
|
||||
ubsan = rt.overrideAttrs (old: {
|
||||
pname = "ovni-ubsan";
|
||||
cmakeFlags = old.cmakeFlags ++ [ "-DCMAKE_BUILD_TYPE=Ubsan" ];
|
||||
});
|
||||
|
||||
armv7 = (pkgs.pkgsCross.armv7l-hf-multiplatform.ovniLocal.overrideAttrs (old: {
|
||||
pname = "ovni-armv7";
|
||||
buildInputs = [];
|
||||
nativeBuildInputs = [ pkgs.pkgsCross.armv7l-hf-multiplatform.buildPackages.cmake ];
|
||||
cmakeFlags = old.cmakeFlags ++ [ "-DUSE_MPI=OFF" ];
|
||||
})).overrideDerivation (old: {
|
||||
doCheck = true;
|
||||
});
|
||||
|
||||
aarch64 = (pkgs.pkgsCross.aarch64-multiplatform.ovniLocal.overrideAttrs (old: {
|
||||
pname = "ovni-aarch64";
|
||||
buildInputs = [];
|
||||
nativeBuildInputs = [ pkgs.pkgsCross.aarch64-multiplatform.buildPackages.cmake ];
|
||||
cmakeFlags = old.cmakeFlags ++ [ "-DUSE_MPI=OFF" ];
|
||||
})).overrideDerivation (old: {
|
||||
doCheck = true;
|
||||
});
|
||||
|
||||
riscv64 = (pkgs.pkgsCross.riscv64.ovniLocal.overrideAttrs (old: {
|
||||
pname = "ovni-riscv64";
|
||||
buildInputs = [];
|
||||
nativeBuildInputs = [ pkgs.pkgsCross.riscv64.buildPackages.cmake ];
|
||||
cmakeFlags = old.cmakeFlags ++ [ "-DUSE_MPI=OFF" ];
|
||||
})).overrideDerivation (old: {
|
||||
doCheck = true;
|
||||
});
|
||||
};
|
||||
};
|
||||
}
|
||||
|
@ -1,4 +1,4 @@
|
||||
/* Copyright (c) 2021-2023 Barcelona Supercomputing Center (BSC)
|
||||
/* Copyright (c) 2021-2024 Barcelona Supercomputing Center (BSC)
|
||||
* SPDX-License-Identifier: MIT */
|
||||
|
||||
#ifndef OVNI_H
|
||||
@ -18,7 +18,7 @@ extern "C" {
|
||||
#include <string.h>
|
||||
#include <sys/types.h>
|
||||
|
||||
#define OVNI_METADATA_VERSION 2
|
||||
#define OVNI_METADATA_VERSION 3
|
||||
|
||||
#define OVNI_TRACEDIR "ovni"
|
||||
#define OVNI_MAX_HOSTNAME 512
|
||||
@ -31,6 +31,9 @@ extern "C" {
|
||||
|
||||
#define OVNI_STREAM_EXT ".obs"
|
||||
|
||||
/* Version of the ovni model for events */
|
||||
#define OVNI_MODEL_VERSION "1.1.0"
|
||||
|
||||
/* Follow https://semver.org rules for versioning */
|
||||
#define OVNI_LIB_VERSION "@PROJECT_VERSION@"
|
||||
#define OVNI_GIT_COMMIT "@OVNI_GIT_COMMIT@"
|
||||
@ -130,6 +133,28 @@ void ovni_ev_jumbo_emit(struct ovni_ev *ev, const uint8_t *buf, uint32_t bufsize
|
||||
|
||||
void ovni_flush(void);
|
||||
|
||||
/* Attributes */
|
||||
int ovni_attr_has(const char *key);
|
||||
void ovni_attr_set_double(const char *key, double num);
|
||||
void ovni_attr_set_boolean(const char *key, int value);
|
||||
void ovni_attr_set_str(const char *key, const char *value);
|
||||
void ovni_attr_set_json(const char *key, const char *json);
|
||||
double ovni_attr_get_double(const char *key);
|
||||
int ovni_attr_get_boolean(const char *key);
|
||||
const char *ovni_attr_get_str(const char *key);
|
||||
char *ovni_attr_get_json(const char *key);
|
||||
void ovni_attr_flush(void);
|
||||
|
||||
/* Mark */
|
||||
enum ovni_mark_flags {
|
||||
OVNI_MARK_STACK = 1, /*< Use push/pop instead of set */
|
||||
};
|
||||
void ovni_mark_type(int32_t type, long flags, const char *title);
|
||||
void ovni_mark_label(int32_t type, int64_t value, const char *label);
|
||||
void ovni_mark_push(int32_t type, int64_t value);
|
||||
void ovni_mark_pop(int32_t type, int64_t value);
|
||||
void ovni_mark_set(int32_t type, int64_t value);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
@ -23,10 +23,14 @@ markdown_extensions:
|
||||
nav:
|
||||
- index.md
|
||||
- 'User guide':
|
||||
- user/concepts.md
|
||||
- user/installation.md
|
||||
- 'Concepts':
|
||||
- user/concepts/part-model.md
|
||||
- user/concepts/trace.md
|
||||
- 'Runtime':
|
||||
- user/runtime/tracing.md
|
||||
- user/runtime/index.md
|
||||
- user/runtime/env.md
|
||||
- user/runtime/mark.md
|
||||
- user/runtime/distributed.md
|
||||
- user/runtime/kernel.md
|
||||
- user/runtime/trace_spec.md
|
||||
|
@ -1,4 +1,4 @@
|
||||
/* Copyright (c) 2021-2023 Barcelona Supercomputing Center (BSC)
|
||||
/* Copyright (c) 2021-2024 Barcelona Supercomputing Center (BSC)
|
||||
* SPDX-License-Identifier: MIT */
|
||||
|
||||
#include "common.h"
|
||||
@ -45,7 +45,7 @@ vaerr(const char *prefix, const char *func, const char *errstr, va_list ap)
|
||||
|
||||
vfprintf(stderr, errstr, ap);
|
||||
|
||||
int len = strlen(errstr);
|
||||
int len = (int) strlen(errstr);
|
||||
|
||||
if (len > 0) {
|
||||
char last = errstr[len - 1];
|
||||
@ -103,7 +103,7 @@ mkpath(const char *path, mode_t mode, int is_dir)
|
||||
char *copypath = strdup(path);
|
||||
|
||||
/* Remove trailing slash */
|
||||
int last = strlen(path) - 1;
|
||||
int last = (int) strlen(path) - 1;
|
||||
while (last > 0 && copypath[last] == '/')
|
||||
copypath[last--] = '\0';
|
||||
|
||||
|
@ -6,6 +6,7 @@
|
||||
|
||||
#include <stdio.h>
|
||||
#include <sys/types.h>
|
||||
#include <inttypes.h>
|
||||
|
||||
extern int is_debug_enabled;
|
||||
|
||||
@ -18,8 +19,8 @@ int mkpath(const char *path, mode_t mode, int is_dir);
|
||||
void progname_set(char *name);
|
||||
const char *progname_get(void);
|
||||
void enable_debug(void);
|
||||
void verr(const char *prefix, const char *func, const char *errstr, ...);
|
||||
void vdie(const char *prefix, const char *func, const char *errstr, ...);
|
||||
void verr(const char *prefix, const char *func, const char *errstr, ...) __attribute__((format(printf, 3, 4)));
|
||||
void vdie(const char *prefix, const char *func, const char *errstr, ...) __attribute__((format(printf, 3, 4)));
|
||||
|
||||
/* clang-format off */
|
||||
|
||||
|
@ -30,7 +30,6 @@ add_library(emu STATIC
|
||||
stream.c
|
||||
trace.c
|
||||
loom.c
|
||||
metadata.c
|
||||
mux.c
|
||||
sort.c
|
||||
path.c
|
||||
@ -49,9 +48,11 @@ add_library(emu STATIC
|
||||
value.c
|
||||
ovni/event.c
|
||||
ovni/setup.c
|
||||
ovni/mark.c
|
||||
nanos6/setup.c
|
||||
nanos6/event.c
|
||||
nanos6/breakdown.c
|
||||
nosv/breakdown.c
|
||||
nosv/setup.c
|
||||
nosv/event.c
|
||||
nodes/setup.c
|
||||
|
@ -1,4 +1,4 @@
|
||||
/* Copyright (c) 2021-2023 Barcelona Supercomputing Center (BSC)
|
||||
/* Copyright (c) 2021-2024 Barcelona Supercomputing Center (BSC)
|
||||
* SPDX-License-Identifier: GPL-3.0-or-later */
|
||||
|
||||
#include "bay.h"
|
||||
@ -106,7 +106,7 @@ bay_add_cb(struct bay *bay, enum bay_cb_type type,
|
||||
cb->func = func;
|
||||
cb->arg = arg;
|
||||
cb->bchan = bchan;
|
||||
cb->type = type;
|
||||
cb->type = (int) type;
|
||||
cb->enabled = 0;
|
||||
|
||||
if (enabled)
|
||||
@ -168,7 +168,7 @@ propagate_chan(struct bay_chan *bchan, enum bay_cb_type type)
|
||||
struct bay_cb *cur = NULL;
|
||||
/* New callbacks cannot be added while propagating a bay_chan */
|
||||
DL_FOREACH(bchan->cb[type], cur) {
|
||||
dbg("calling cb %p", cur->func);
|
||||
dbg("calling cb %"PRIxPTR, (uintptr_t) cur->func);
|
||||
if (cur->func(bchan->chan, cur->arg) != 0) {
|
||||
err("callback failed for %s", bchan->chan->name);
|
||||
return -1;
|
||||
|
@ -1,4 +1,4 @@
|
||||
/* Copyright (c) 2023 Barcelona Supercomputing Center (BSC)
|
||||
/* Copyright (c) 2023-2024 Barcelona Supercomputing Center (BSC)
|
||||
* SPDX-License-Identifier: GPL-3.0-or-later */
|
||||
|
||||
#include "body.h"
|
||||
|
@ -1,4 +1,4 @@
|
||||
/* Copyright (c) 2021-2023 Barcelona Supercomputing Center (BSC)
|
||||
/* Copyright (c) 2021-2024 Barcelona Supercomputing Center (BSC)
|
||||
* SPDX-License-Identifier: GPL-3.0-or-later */
|
||||
|
||||
#include "chan.h"
|
||||
@ -15,9 +15,11 @@ chan_init(struct chan *chan, enum chan_type type, const char *fmt, ...)
|
||||
va_list ap;
|
||||
va_start(ap, fmt);
|
||||
|
||||
int n = ARRAYLEN(chan->name);
|
||||
size_t n = ARRAYLEN(chan->name);
|
||||
int ret = vsnprintf(chan->name, n, fmt, ap);
|
||||
if (ret >= n)
|
||||
if (ret < 0)
|
||||
die("vsnprintf failed");
|
||||
else if ((size_t) ret >= n)
|
||||
die("channel name too long");
|
||||
va_end(ap);
|
||||
|
||||
@ -155,7 +157,7 @@ chan_push(struct chan *chan, struct value value)
|
||||
/** Remove one value from the stack. Fails if the top of the stack
|
||||
* doesn't match the expected value.
|
||||
*
|
||||
* @param expected The expected value on the top of the stack.
|
||||
* @param evalue The expected value on the top of the stack.
|
||||
*
|
||||
* @return On success returns 0, otherwise returns -1.
|
||||
*/
|
||||
@ -182,10 +184,10 @@ chan_pop(struct chan *chan, struct value evalue)
|
||||
struct value *value = &stack->values[stack->n - 1];
|
||||
|
||||
if (!value_is_equal(value, &evalue)) {
|
||||
err("%s: unexpected value %s (expected %s)",
|
||||
err("%s: expected value %s different from top of stack %s",
|
||||
chan->name,
|
||||
value_str(*value),
|
||||
value_str(evalue));
|
||||
value_str(evalue),
|
||||
value_str(*value));
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
@ -1,4 +1,4 @@
|
||||
/* Copyright (c) 2021-2023 Barcelona Supercomputing Center (BSC)
|
||||
/* Copyright (c) 2021-2024 Barcelona Supercomputing Center (BSC)
|
||||
* SPDX-License-Identifier: GPL-3.0-or-later */
|
||||
|
||||
#ifndef CHAN_H
|
||||
@ -70,7 +70,7 @@ chan_read(struct chan *chan, struct value *value)
|
||||
return 0;
|
||||
}
|
||||
|
||||
void chan_init(struct chan *chan, enum chan_type type, const char *fmt, ...);
|
||||
void chan_init(struct chan *chan, enum chan_type type, const char *fmt, ...) __attribute__((format(printf, 3, 4)));
|
||||
USE_RET int chan_set(struct chan *chan, struct value value);
|
||||
USE_RET int chan_push(struct chan *chan, struct value value);
|
||||
USE_RET int chan_pop(struct chan *chan, struct value expected);
|
||||
|
@ -1,4 +1,4 @@
|
||||
/* Copyright (c) 2021-2023 Barcelona Supercomputing Center (BSC)
|
||||
/* Copyright (c) 2021-2024 Barcelona Supercomputing Center (BSC)
|
||||
* SPDX-License-Identifier: GPL-3.0-or-later */
|
||||
|
||||
#include "clkoff.h"
|
||||
@ -59,7 +59,7 @@ cparse(struct clkoff *table, FILE *file)
|
||||
if (buf[0] == '\n')
|
||||
continue;
|
||||
|
||||
int ret = sscanf(buf, "%ld %s %lf %lf %lf",
|
||||
int ret = sscanf(buf, "%" SCNd64 "%s %lf %lf %lf",
|
||||
&e.index, e.name,
|
||||
&e.median, &e.mean, &e.stdev);
|
||||
|
||||
@ -95,7 +95,7 @@ cindex(struct clkoff *table)
|
||||
return -1;
|
||||
}
|
||||
|
||||
table->index = calloc(table->nentries, sizeof(struct clkoff_entry *));
|
||||
table->index = calloc((size_t) table->nentries, sizeof(struct clkoff_entry *));
|
||||
|
||||
if (table->index == NULL) {
|
||||
err("calloc failed");
|
||||
|
@ -1,4 +1,4 @@
|
||||
/* Copyright (c) 2021-2023 Barcelona Supercomputing Center (BSC)
|
||||
/* Copyright (c) 2021-2024 Barcelona Supercomputing Center (BSC)
|
||||
* SPDX-License-Identifier: GPL-3.0-or-later */
|
||||
|
||||
#include "cpu.h"
|
||||
@ -17,7 +17,7 @@
|
||||
#include "utlist.h"
|
||||
#include "value.h"
|
||||
|
||||
static const char chan_fmt[] = "cpu%ld.%s";
|
||||
static const char chan_fmt[] = "cpu%"PRIi64".%s";
|
||||
static const char *chan_name[CPU_CHAN_MAX] = {
|
||||
[CPU_CHAN_NRUN] = "nrunning",
|
||||
[CPU_CHAN_PID] = "pid_running",
|
||||
@ -78,14 +78,14 @@ cpu_set_loom(struct cpu *cpu, struct loom *loom)
|
||||
static int
|
||||
set_name(struct cpu *cpu)
|
||||
{
|
||||
size_t i = loom_get_gindex(cpu->loom);
|
||||
size_t j = cpu_get_phyid(cpu);
|
||||
size_t i = (size_t) loom_get_gindex(cpu->loom);
|
||||
size_t j = (size_t) cpu_get_phyid(cpu);
|
||||
int n;
|
||||
|
||||
if (cpu->is_virtual)
|
||||
n = snprintf(cpu->name, PATH_MAX, "vCPU %ld.*", i);
|
||||
n = snprintf(cpu->name, PATH_MAX, "vCPU %zu.*", i);
|
||||
else
|
||||
n = snprintf(cpu->name, PATH_MAX, " CPU %ld.%ld", i, j);
|
||||
n = snprintf(cpu->name, PATH_MAX, " CPU %zu.%zu", i, j);
|
||||
|
||||
if (n >= PATH_MAX) {
|
||||
err("cpu name too long");
|
||||
@ -162,7 +162,7 @@ cpu_connect(struct cpu *cpu, struct bay *bay, struct recorder *rec)
|
||||
if (type < 0)
|
||||
continue;
|
||||
|
||||
long row = cpu->gindex;
|
||||
long row = (long) cpu->gindex;
|
||||
long flags = prv_flags[i];
|
||||
if (prv_register(prv, row, type, bay, c, flags)) {
|
||||
err("prv_register failed");
|
||||
@ -176,7 +176,7 @@ cpu_connect(struct cpu *cpu, struct bay *bay, struct recorder *rec)
|
||||
struct pcf_value *
|
||||
cpu_add_to_pcf_type(struct cpu *cpu, struct pcf_type *type)
|
||||
{
|
||||
return pcf_add_value(type, cpu->gindex + 1, cpu->name);
|
||||
return pcf_add_value(type, (int) cpu->gindex + 1, cpu->name);
|
||||
}
|
||||
|
||||
static struct thread *
|
||||
@ -215,12 +215,12 @@ cpu_update(struct cpu *cpu)
|
||||
}
|
||||
}
|
||||
|
||||
cpu->nth_running = running;
|
||||
cpu->nth_active = active;
|
||||
cpu->nth_running = (size_t) running;
|
||||
cpu->nth_active = (size_t) active;
|
||||
|
||||
/* Only virtual cpus can be oversubscribed */
|
||||
if (cpu->nth_running > 1 && !cpu->is_virtual) {
|
||||
err("physical cpu %s has %d threads running at the same time",
|
||||
err("physical cpu %s has %zd threads running at the same time",
|
||||
cpu->name, cpu->nth_running);
|
||||
return -1;
|
||||
}
|
||||
@ -248,7 +248,7 @@ cpu_update(struct cpu *cpu)
|
||||
err("chan_set pid failed");
|
||||
return -1;
|
||||
}
|
||||
dbg("cpu%ld sets th_running to %s",
|
||||
dbg("cpu%"PRIi64" sets th_running to %s",
|
||||
cpu->gindex, value_str(gid_running));
|
||||
if (chan_set(&cpu->chan[CPU_CHAN_THRUN], gid_running) != 0) {
|
||||
err("chan_set gid_running failed");
|
||||
@ -265,7 +265,7 @@ cpu_update(struct cpu *cpu)
|
||||
}
|
||||
|
||||
/* Update nth_running number in the channel */
|
||||
if (chan_set(&cpu->chan[CPU_CHAN_NRUN], value_int64(running)) != 0) {
|
||||
if (chan_set(&cpu->chan[CPU_CHAN_NRUN], value_int64((int64_t) running)) != 0) {
|
||||
err("chan_set nth_running failed");
|
||||
return -1;
|
||||
}
|
||||
|
@ -1,4 +1,4 @@
|
||||
/* Copyright (c) 2021-2023 Barcelona Supercomputing Center (BSC)
|
||||
/* Copyright (c) 2021-2024 Barcelona Supercomputing Center (BSC)
|
||||
* SPDX-License-Identifier: GPL-3.0-or-later */
|
||||
|
||||
#include "emu.h"
|
||||
@ -118,18 +118,18 @@ panic(struct emu *emu)
|
||||
if (emu->ev != NULL) {
|
||||
err("event: ");
|
||||
err(" mcv=%s", emu->ev->mcv);
|
||||
err(" rclock=%ld", emu->ev->rclock);
|
||||
err(" sclock=%ld", emu->ev->sclock);
|
||||
err(" dclock=%ld", emu->ev->dclock);
|
||||
err(" payload_size=%ld", emu->ev->payload_size);
|
||||
err(" rclock=%"PRIi64, emu->ev->rclock);
|
||||
err(" sclock=%"PRIi64, emu->ev->sclock);
|
||||
err(" dclock=%"PRIi64, emu->ev->dclock);
|
||||
err(" payload_size=%zd", emu->ev->payload_size);
|
||||
err(" is_jumbo=%d", emu->ev->is_jumbo);
|
||||
}
|
||||
|
||||
if (emu->stream != NULL) {
|
||||
err("stream: ");
|
||||
err(" relpath=%s", emu->stream->relpath);
|
||||
err(" offset=%ld", emu->stream->offset);
|
||||
err(" clock_offset=%ld", emu->stream->clock_offset);
|
||||
err(" offset=%"PRIi64, emu->stream->offset);
|
||||
err(" clock_offset=%"PRIi64, emu->stream->clock_offset);
|
||||
}
|
||||
err("@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@");
|
||||
}
|
||||
@ -156,7 +156,7 @@ emu_step(struct emu *emu)
|
||||
return -1;
|
||||
}
|
||||
|
||||
dbg("----- mvc=%s dclock=%ld -----", emu->ev->mcv, emu->ev->dclock);
|
||||
dbg("----- mcv=%s dclock=%"PRIi64" -----", emu->ev->mcv, emu->ev->dclock);
|
||||
|
||||
emu_stat_update(&emu->stat, &emu->player);
|
||||
|
||||
|
@ -1,4 +1,4 @@
|
||||
/* Copyright (c) 2021-2023 Barcelona Supercomputing Center (BSC)
|
||||
/* Copyright (c) 2021-2024 Barcelona Supercomputing Center (BSC)
|
||||
* SPDX-License-Identifier: GPL-3.0-or-later */
|
||||
|
||||
#include "emu_ev.h"
|
||||
@ -13,11 +13,11 @@ emu_ev(struct emu_ev *ev, const struct ovni_ev *oev,
|
||||
ev->v = oev->header.value;
|
||||
ev->mcv[3] = '\0';
|
||||
|
||||
ev->rclock = oev->header.clock;
|
||||
ev->rclock = (int64_t) oev->header.clock;
|
||||
ev->sclock = sclock;
|
||||
ev->dclock = dclock;
|
||||
|
||||
ev->payload_size = ovni_payload_size(oev);
|
||||
ev->payload_size = (size_t) ovni_payload_size(oev);
|
||||
|
||||
if (ev->payload_size > 0) {
|
||||
ev->has_payload = 1;
|
||||
|
@ -1,4 +1,4 @@
|
||||
/* Copyright (c) 2021-2023 Barcelona Supercomputing Center (BSC)
|
||||
/* Copyright (c) 2021-2024 Barcelona Supercomputing Center (BSC)
|
||||
* SPDX-License-Identifier: GPL-3.0-or-later */
|
||||
|
||||
#ifndef EMU_PRV_H
|
||||
@ -19,6 +19,8 @@ enum emu_prv_types {
|
||||
PRV_NOSV_SUBSYSTEM = 13,
|
||||
PRV_NOSV_RANK = 14,
|
||||
PRV_NOSV_BODYID = 15,
|
||||
PRV_NOSV_IDLE = 16,
|
||||
PRV_NOSV_BREAKDOWN = 17,
|
||||
PRV_TAMPI_SUBSYSTEM = 20,
|
||||
PRV_MPI_FUNCTION = 25,
|
||||
PRV_NODES_SUBSYSTEM = 30,
|
||||
@ -31,7 +33,11 @@ enum emu_prv_types {
|
||||
PRV_NANOS6_BREAKDOWN = 41,
|
||||
PRV_KERNEL_CS = 45,
|
||||
PRV_OPENMP_SUBSYSTEM = 50,
|
||||
PRV_RESERVED = 100,
|
||||
PRV_OPENMP_LABEL = 51,
|
||||
PRV_OPENMP_TASKID = 52,
|
||||
PRV_OVNI_MARK = 100,
|
||||
/* User marks [100, 200) */
|
||||
PRV_RESERVED = 200,
|
||||
};
|
||||
|
||||
#endif /* EMU_PRV_H */
|
||||
|
@ -1,4 +1,4 @@
|
||||
/* Copyright (c) 2021-2023 Barcelona Supercomputing Center (BSC)
|
||||
/* Copyright (c) 2021-2024 Barcelona Supercomputing Center (BSC)
|
||||
* SPDX-License-Identifier: GPL-3.0-or-later */
|
||||
|
||||
#include "emu_stat.h"
|
||||
@ -54,11 +54,9 @@ emu_stat_report(struct emu_stat *stat, struct player *player, int last)
|
||||
double speed = stat->average ? avgspeed : instspeed;
|
||||
|
||||
if (last) {
|
||||
int tmin = (int) (time_elapsed / 60.0);
|
||||
int tsec = (int) ((time_elapsed / 60.0 - tmin) * 60.0);
|
||||
info("%5.1f%% done at avg %.0f kev/s \n",
|
||||
progress * 100.0, avgspeed * 1e-3, tmin, tsec);
|
||||
info("processed %ld input events in %.2f s\n",
|
||||
progress * 100.0, avgspeed * 1e-3);
|
||||
info("processed %"PRIi64" input events in %.2f s\n",
|
||||
nprocessed, time_elapsed);
|
||||
} else {
|
||||
int tmin = (int) (time_left / 60.0);
|
||||
|
@ -104,7 +104,7 @@ parse_arg(struct ev_spec *spec, char *arg)
|
||||
}
|
||||
|
||||
/* Copy name */
|
||||
size_t n = snprintf(argspec->name, sizeof(argspec->name), "%s", name);
|
||||
size_t n = (size_t) snprintf(argspec->name, sizeof(argspec->name), "%s", name);
|
||||
if (n >= sizeof(argspec->name)) {
|
||||
err("argument name too long: %s", name);
|
||||
return -1;
|
||||
@ -363,8 +363,9 @@ print_arg(struct ev_arg *arg, const char *fmt, struct cursor *c, struct emu_ev *
|
||||
|
||||
#define CASE(TYPE) \
|
||||
do { \
|
||||
TYPE *data = (TYPE *) &payload[arg->offset]; \
|
||||
n = snprintf(c->out, c->len, fmt, *data); \
|
||||
TYPE data; \
|
||||
memcpy(&data, &payload[arg->offset], sizeof(data)); \
|
||||
n = snprintf(c->out, (size_t) c->len, fmt, data); \
|
||||
if (n >= c->len) { \
|
||||
err("no space for argument"); \
|
||||
return -1; \
|
||||
@ -386,8 +387,8 @@ print_arg(struct ev_arg *arg, const char *fmt, struct cursor *c, struct emu_ev *
|
||||
char *data = (char *) &payload[arg->offset];
|
||||
/* Here we trust the input string to
|
||||
* contain a nil at the end */
|
||||
int n = snprintf(c->out, c->len, fmt, data);
|
||||
if (n >= c->len) {
|
||||
int n = snprintf(c->out, (size_t) c->len, fmt, data);
|
||||
if (n >= (int) c->len) {
|
||||
err("no space for string argument");
|
||||
return -1;
|
||||
}
|
||||
|
220
src/emu/loom.c
220
src/emu/loom.c
@ -1,4 +1,4 @@
|
||||
/* Copyright (c) 2021-2023 Barcelona Supercomputing Center (BSC)
|
||||
/* Copyright (c) 2021-2024 Barcelona Supercomputing Center (BSC)
|
||||
* SPDX-License-Identifier: GPL-3.0-or-later */
|
||||
|
||||
#include "loom.h"
|
||||
@ -8,15 +8,13 @@
|
||||
#include "cpu.h"
|
||||
#include "path.h"
|
||||
#include "proc.h"
|
||||
#include "stream.h"
|
||||
#include "uthash.h"
|
||||
|
||||
static const char *loom_prefix = "loom.";
|
||||
|
||||
static void
|
||||
set_hostname(char host[PATH_MAX], const char name[PATH_MAX])
|
||||
{
|
||||
/* Skip prefix */
|
||||
const char *start = name + strlen(loom_prefix);
|
||||
const char *start = name;
|
||||
|
||||
/* Copy until dot or end */
|
||||
int i;
|
||||
@ -30,10 +28,19 @@ set_hostname(char host[PATH_MAX], const char name[PATH_MAX])
|
||||
host[i] = '\0';
|
||||
}
|
||||
|
||||
int
|
||||
loom_matches(const char *path)
|
||||
const char *
|
||||
loom_name(struct stream *s)
|
||||
{
|
||||
return path_has_prefix(path, loom_prefix);
|
||||
JSON_Object *meta = stream_metadata(s);
|
||||
const char *loom = json_object_dotget_string(meta, "ovni.loom");
|
||||
|
||||
if (loom == NULL) {
|
||||
err("cannot get attribute ovni.loom for stream: %s",
|
||||
s->relpath);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return loom;
|
||||
}
|
||||
|
||||
int
|
||||
@ -41,11 +48,6 @@ loom_init_begin(struct loom *loom, const char *name)
|
||||
{
|
||||
memset(loom, 0, sizeof(struct loom));
|
||||
|
||||
if (!path_has_prefix(name, loom_prefix)) {
|
||||
err("loom name must start with '%s': %s", loom_prefix, name);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (strchr(name, '/') != NULL) {
|
||||
err("loom name cannot contain '/': %s", name);
|
||||
return -1;
|
||||
@ -68,6 +70,99 @@ loom_init_begin(struct loom *loom, const char *name)
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Merges the metadata CPUs with the ones in the loom */
|
||||
static int
|
||||
load_cpus(struct loom *loom, JSON_Object *meta)
|
||||
{
|
||||
JSON_Array *cpuarray = json_object_dotget_array(meta, "ovni.loom_cpus");
|
||||
|
||||
/* It may not have the CPUs defined */
|
||||
if (cpuarray == NULL)
|
||||
return 0;
|
||||
|
||||
size_t ncpus = json_array_get_count(cpuarray);
|
||||
if (ncpus == 0) {
|
||||
err("empty 'cpus' array in metadata");
|
||||
return -1;
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < ncpus; i++) {
|
||||
JSON_Object *jcpu = json_array_get_object(cpuarray, i);
|
||||
if (jcpu == NULL) {
|
||||
err("json_array_get_object() failed for cpu");
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Cast from double */
|
||||
int index = (int) json_object_get_number(jcpu, "index");
|
||||
int phyid = (int) json_object_get_number(jcpu, "phyid");
|
||||
|
||||
/* The index can exceed ncpus-1 when CPUs are partially
|
||||
* defined, but it cannot be negative. */
|
||||
if (index < 0) {
|
||||
err("cpu index %d out of bounds", index);
|
||||
return -1;
|
||||
}
|
||||
|
||||
struct cpu *cpu = loom_find_cpu(loom, phyid);
|
||||
|
||||
if (cpu) {
|
||||
/* Ensure they have the same index */
|
||||
if (cpu->index != index) {
|
||||
err("mismatch index in existing cpu: %d", index);
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Duplicated, ignore */
|
||||
continue;
|
||||
}
|
||||
|
||||
/* If we reach this point, there shouldn't be a CPU with the
|
||||
* same index either, as otherwise the phyid should have matched
|
||||
* before. So it is an error. */
|
||||
if (loom_get_cpu(loom, index) != NULL) {
|
||||
err("cpu index %d redefined with another phyid", index);
|
||||
return -1;
|
||||
}
|
||||
|
||||
cpu = calloc(1, sizeof(struct cpu));
|
||||
if (cpu == NULL) {
|
||||
err("calloc failed:");
|
||||
return -1;
|
||||
}
|
||||
|
||||
cpu_init_begin(cpu, index, phyid, 0);
|
||||
|
||||
if (loom_add_cpu(loom, cpu) != 0) {
|
||||
err("loom_add_cpu() failed");
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/** Merges the given metadata with the one stored.
|
||||
*
|
||||
* It is an error to provide metadata that doesn't match with the already stored
|
||||
* in the process.
|
||||
*
|
||||
* Precondition: The stream ovni.part must be "thread".
|
||||
* Precondition: The stream version must be ok.
|
||||
*/
|
||||
int
|
||||
loom_load_metadata(struct loom *loom, struct stream *s)
|
||||
{
|
||||
JSON_Object *meta = stream_metadata(s);
|
||||
|
||||
if (load_cpus(loom, meta) != 0) {
|
||||
err("cannot load loom cpus");
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void
|
||||
loom_set_gindex(struct loom *loom, int64_t gindex)
|
||||
{
|
||||
@ -97,10 +192,8 @@ loom_get_cpu(struct loom *loom, int index)
|
||||
if (index == -1)
|
||||
return &loom->vcpu;
|
||||
|
||||
if (index < 0 || (size_t) index >= loom->ncpus) {
|
||||
err("cpu index out of bounds");
|
||||
if (index < 0 || (size_t) index >= loom->ncpus)
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return loom->cpus_array[index];
|
||||
}
|
||||
@ -181,6 +274,44 @@ by_phyid(struct cpu *c1, struct cpu *c2)
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
loom_set_rank_min(struct loom *loom)
|
||||
{
|
||||
if (loom->rank_min != INT_MAX) {
|
||||
err("rank_min already set");
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Ensure that all processes have a rank */
|
||||
for (struct proc *p = loom->procs; p; p = p->hh.next) {
|
||||
if (p->rank >= 0) {
|
||||
loom->rank_enabled = 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!loom->rank_enabled) {
|
||||
dbg("loom %s has no rank information", loom->name);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Ensure that all processes have a rank */
|
||||
for (struct proc *p = loom->procs; p; p = p->hh.next) {
|
||||
if (p->rank < 0) {
|
||||
err("process %s has no rank information", p->id);
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Compute rank_min for CPU sorting */
|
||||
if (p->rank < loom->rank_min)
|
||||
loom->rank_min = p->rank;
|
||||
}
|
||||
|
||||
dbg("loom %s has rank_min %d", loom->name, loom->rank_min);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void
|
||||
loom_sort(struct loom *loom)
|
||||
{
|
||||
@ -198,22 +329,22 @@ loom_sort(struct loom *loom)
|
||||
int
|
||||
loom_init_end(struct loom *loom)
|
||||
{
|
||||
/* Set rank enabled */
|
||||
for (struct proc *p = loom->procs; p; p = p->hh.next) {
|
||||
if (p->rank >= 0) {
|
||||
loom->rank_enabled = 1;
|
||||
break;
|
||||
}
|
||||
/* rank_min must be set */
|
||||
if (loom->rank_enabled && loom->rank_min == INT_MAX) {
|
||||
err("rank_min not set");
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Ensure that all processes have a rank */
|
||||
if (loom->rank_enabled) {
|
||||
for (struct proc *p = loom->procs; p; p = p->hh.next) {
|
||||
if (p->rank < 0) {
|
||||
err("process %s has no rank information", p->id);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
/* It is not valid to define a loom without CPUs */
|
||||
if (loom->ncpus == 0) {
|
||||
err("loom %s has no physical CPUs", loom->name);
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Or without processes */
|
||||
if (loom->nprocs == 0) {
|
||||
err("loom %s has no processes", loom->name);
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Populate cpus_array */
|
||||
@ -222,6 +353,7 @@ loom_init_end(struct loom *loom)
|
||||
err("calloc failed:");
|
||||
return -1;
|
||||
}
|
||||
|
||||
for (struct cpu *c = loom->cpus; c; c = c->hh.next) {
|
||||
int index = cpu_get_index(c);
|
||||
if (index < 0 || (size_t) index >= loom->ncpus) {
|
||||
@ -277,34 +409,6 @@ loom_add_proc(struct loom *loom, struct proc *proc)
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (!proc->metadata_loaded) {
|
||||
err("process %d hasn't loaded metadata", pid);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (loom->rank_enabled && proc->rank < 0) {
|
||||
err("missing rank in process %d", pid);
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Check previous ranks if any */
|
||||
if (!loom->rank_enabled && proc->rank >= 0) {
|
||||
loom->rank_enabled = 1;
|
||||
|
||||
for (struct proc *p = loom->procs; p; p = p->hh.next) {
|
||||
if (p->rank < 0) {
|
||||
err("missing rank in process %d", p->pid);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (p->rank < loom->rank_min)
|
||||
loom->rank_min = p->rank;
|
||||
}
|
||||
}
|
||||
|
||||
if (loom->rank_enabled && proc->rank < loom->rank_min)
|
||||
loom->rank_min = proc->rank;
|
||||
|
||||
HASH_ADD_INT(loom->procs, pid, proc);
|
||||
loom->nprocs++;
|
||||
|
||||
|
@ -1,4 +1,4 @@
|
||||
/* Copyright (c) 2021-2023 Barcelona Supercomputing Center (BSC)
|
||||
/* Copyright (c) 2021-2024 Barcelona Supercomputing Center (BSC)
|
||||
* SPDX-License-Identifier: GPL-3.0-or-later */
|
||||
|
||||
#ifndef LOOM_H
|
||||
@ -12,9 +12,10 @@
|
||||
#include "cpu.h"
|
||||
#include "extend.h"
|
||||
struct proc;
|
||||
struct stream;
|
||||
|
||||
struct loom {
|
||||
size_t gindex;
|
||||
int64_t gindex;
|
||||
int is_init;
|
||||
|
||||
char name[PATH_MAX];
|
||||
@ -50,8 +51,10 @@ struct loom {
|
||||
struct extend ext;
|
||||
};
|
||||
|
||||
USE_RET int loom_matches(const char *relpath);
|
||||
USE_RET const char *loom_name(struct stream *s);
|
||||
USE_RET int loom_init_begin(struct loom *loom, const char *name);
|
||||
USE_RET int loom_load_metadata(struct loom *loom, struct stream *s);
|
||||
USE_RET int loom_set_rank_min(struct loom *loom);
|
||||
USE_RET int loom_init_end(struct loom *loom);
|
||||
USE_RET int loom_add_cpu(struct loom *loom, struct cpu *cpu);
|
||||
USE_RET int64_t loom_get_gindex(struct loom *loom);
|
||||
|
@ -1,159 +0,0 @@
|
||||
/* Copyright (c) 2021-2023 Barcelona Supercomputing Center (BSC)
|
||||
* SPDX-License-Identifier: GPL-3.0-or-later */
|
||||
|
||||
#include "metadata.h"
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include "cpu.h"
|
||||
#include "loom.h"
|
||||
#include "ovni.h"
|
||||
#include "parson.h"
|
||||
#include "proc.h"
|
||||
#include "thread.h"
|
||||
|
||||
static JSON_Object *
|
||||
load_json(const char *path)
|
||||
{
|
||||
JSON_Value *vmeta = json_parse_file_with_comments(path);
|
||||
if (vmeta == NULL) {
|
||||
err("json_parse_file_with_comments() failed");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
JSON_Object *meta = json_value_get_object(vmeta);
|
||||
if (meta == NULL) {
|
||||
err("json_value_get_object() failed");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return meta;
|
||||
}
|
||||
|
||||
static int
|
||||
check_version(JSON_Object *meta)
|
||||
{
|
||||
JSON_Value *version_val = json_object_get_value(meta, "version");
|
||||
if (version_val == NULL) {
|
||||
err("missing attribute \"version\"");
|
||||
return -1;
|
||||
}
|
||||
|
||||
int version = (int) json_number(version_val);
|
||||
|
||||
if (version != OVNI_METADATA_VERSION) {
|
||||
err("metadata version mismatch %d (expected %d)",
|
||||
version, OVNI_METADATA_VERSION);
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
has_cpus(JSON_Object *meta)
|
||||
{
|
||||
/* Only check for the "cpus" key, if it has zero elements is an error
|
||||
* that will be reported later */
|
||||
if (json_object_get_array(meta, "cpus") != NULL)
|
||||
return 1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
load_cpus(struct loom *loom, JSON_Object *meta)
|
||||
{
|
||||
JSON_Array *cpuarray = json_object_get_array(meta, "cpus");
|
||||
if (cpuarray == NULL) {
|
||||
err("cannot find 'cpus' array");
|
||||
return -1;
|
||||
}
|
||||
|
||||
size_t ncpus = json_array_get_count(cpuarray);
|
||||
if (ncpus == 0) {
|
||||
err("empty 'cpus' array in metadata");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (loom->ncpus > 0) {
|
||||
err("loom %s already has cpus", loom->id);
|
||||
return -1;
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < ncpus; i++) {
|
||||
JSON_Object *jcpu = json_array_get_object(cpuarray, i);
|
||||
if (jcpu == NULL) {
|
||||
err("json_array_get_object() failed for cpu");
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Cast from double */
|
||||
int index = (int) json_object_get_number(jcpu, "index");
|
||||
int phyid = (int) json_object_get_number(jcpu, "phyid");
|
||||
|
||||
struct cpu *cpu = calloc(1, sizeof(struct cpu));
|
||||
if (cpu == NULL) {
|
||||
err("calloc failed:");
|
||||
return -1;
|
||||
}
|
||||
|
||||
cpu_init_begin(cpu, index, phyid, 0);
|
||||
|
||||
if (loom_add_cpu(loom, cpu) != 0) {
|
||||
err("loom_add_cpu() failed");
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
metadata_load_proc(const char *path, struct loom *loom, struct proc *proc)
|
||||
{
|
||||
JSON_Object *meta = load_json(path);
|
||||
if (meta == NULL) {
|
||||
err("cannot load proc metadata from file %s", path);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (check_version(meta) != 0) {
|
||||
err("version check failed");
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* The appid is populated from the metadata */
|
||||
if (proc_load_metadata(proc, meta) != 0) {
|
||||
err("cannot load process attributes");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (has_cpus(meta) && load_cpus(loom, meta) != 0) {
|
||||
err("cannot load loom cpus");
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
metadata_load_thread(const char *path, struct thread *thread)
|
||||
{
|
||||
JSON_Object *meta = load_json(path);
|
||||
if (meta == NULL) {
|
||||
err("cannot load thread metadata from file %s", path);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (check_version(meta) != 0) {
|
||||
err("version check failed");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (thread_load_metadata(thread, meta) != 0) {
|
||||
err("cannot load thread attributes");
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
@ -1,15 +0,0 @@
|
||||
/* Copyright (c) 2021-2023 Barcelona Supercomputing Center (BSC)
|
||||
* SPDX-License-Identifier: GPL-3.0-or-later */
|
||||
|
||||
#ifndef METADATA_H
|
||||
#define METADATA_H
|
||||
|
||||
#include "common.h"
|
||||
struct loom;
|
||||
struct proc;
|
||||
struct thread;
|
||||
|
||||
USE_RET int metadata_load_proc(const char *path, struct loom *loom, struct proc *proc);
|
||||
USE_RET int metadata_load_thread(const char *path, struct thread *thread);
|
||||
|
||||
#endif /* METADATA_H */
|
@ -235,13 +235,6 @@ model_finish(struct model *model, struct emu *emu)
|
||||
static int
|
||||
should_enable(int have[3], struct model_spec *spec, struct thread *t)
|
||||
{
|
||||
static int compat = 0;
|
||||
|
||||
/* Enable all models if we are in compatibility model. Don't check other
|
||||
* threads metadata */
|
||||
if (compat)
|
||||
return 1;
|
||||
|
||||
if (t->meta == NULL) {
|
||||
err("missing metadata for thread %s", t->id);
|
||||
return -1;
|
||||
@ -249,12 +242,8 @@ should_enable(int have[3], struct model_spec *spec, struct thread *t)
|
||||
|
||||
JSON_Object *require = json_object_dotget_object(t->meta, "ovni.require");
|
||||
if (require == NULL) {
|
||||
warn("missing 'ovni.require' key in thread %s", t->id);
|
||||
warn("loading trace in compatibility mode");
|
||||
warn("all models will be enabled (expect slowdown)");
|
||||
warn("use ovni_thread_require() to enable only required models");
|
||||
compat = 1;
|
||||
return 1;
|
||||
err("missing 'ovni.require' key in thread %s", t->id);
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* May not have the current model */
|
||||
|
@ -1,4 +1,4 @@
|
||||
/* Copyright (c) 2021-2023 Barcelona Supercomputing Center (BSC)
|
||||
/* Copyright (c) 2021-2024 Barcelona Supercomputing Center (BSC)
|
||||
* SPDX-License-Identifier: GPL-3.0-or-later */
|
||||
|
||||
#include "model_cpu.h"
|
||||
@ -27,7 +27,7 @@ get_model_cpu(struct cpu *cpu, int id)
|
||||
static int
|
||||
init_chan(struct model_cpu *cpu, const struct model_chan_spec *spec, int64_t gindex)
|
||||
{
|
||||
cpu->track = calloc(spec->nch, sizeof(struct track));
|
||||
cpu->track = calloc((size_t) spec->nch, sizeof(struct track));
|
||||
if (cpu->track == NULL) {
|
||||
err("calloc failed:");
|
||||
return -1;
|
||||
@ -40,7 +40,8 @@ init_chan(struct model_cpu *cpu, const struct model_chan_spec *spec, int64_t gin
|
||||
const char *ch_name = spec->ch_names[i];
|
||||
int track_mode = spec->track[i];
|
||||
|
||||
if (track_init(track, cpu->bay, TRACK_TYPE_TH, track_mode, "%s.cpu%ld.%s",
|
||||
if (track_init(track, cpu->bay, TRACK_TYPE_TH, track_mode,
|
||||
"%s.cpu%"PRIi64".%s",
|
||||
name, gindex, ch_name) != 0) {
|
||||
err("track_init failed");
|
||||
return -1;
|
||||
@ -107,7 +108,7 @@ connect_cpu(struct emu *emu, struct cpu *scpu, int id)
|
||||
|
||||
struct chan *sel = cpu_get_th_chan(scpu);
|
||||
|
||||
int64_t nthreads = emu->system.nthreads;
|
||||
int64_t nthreads = (int64_t) emu->system.nthreads;
|
||||
if (track_set_select(track, sel, NULL, nthreads) != 0) {
|
||||
err("track_select failed");
|
||||
return -1;
|
||||
|
@ -21,7 +21,7 @@ model_evspec_init(struct model_evspec *evspec, struct model_spec *spec)
|
||||
}
|
||||
|
||||
/* Preallocate a contiguous map, as we know the size */
|
||||
evspec->alloc = calloc(evspec->nevents, sizeof(struct ev_spec));
|
||||
evspec->alloc = calloc((size_t) evspec->nevents, sizeof(struct ev_spec));
|
||||
if (evspec->alloc == NULL) {
|
||||
err("calloc failed:");
|
||||
return -1;
|
||||
@ -64,7 +64,7 @@ model_evspec_init(struct model_evspec *evspec, struct model_spec *spec)
|
||||
}
|
||||
|
||||
struct ev_spec *
|
||||
model_evspec_find(struct model_evspec *evspec, char mcv[4])
|
||||
model_evspec_find(struct model_evspec *evspec, char *mcv)
|
||||
{
|
||||
struct ev_spec *s = NULL;
|
||||
HASH_FIND_STR(evspec->spec, mcv, s);
|
||||
|
@ -19,6 +19,6 @@ struct model_evspec {
|
||||
};
|
||||
|
||||
USE_RET int model_evspec_init(struct model_evspec *evspec, struct model_spec *spec);
|
||||
USE_RET struct ev_spec *model_evspec_find(struct model_evspec *evspec, char mcv[4]);
|
||||
USE_RET struct ev_spec *model_evspec_find(struct model_evspec *evspec, char *mcv);
|
||||
|
||||
#endif /* MODEL_EVSPEC_H */
|
||||
|
@ -1,4 +1,4 @@
|
||||
/* Copyright (c) 2021-2023 Barcelona Supercomputing Center (BSC)
|
||||
/* Copyright (c) 2021-2024 Barcelona Supercomputing Center (BSC)
|
||||
* SPDX-License-Identifier: GPL-3.0-or-later */
|
||||
|
||||
#include "model_pvt.h"
|
||||
@ -35,7 +35,7 @@ create_values(const struct model_pvt_spec *pvt,
|
||||
return 0;
|
||||
|
||||
for (const struct pcf_value_label *p = q; p->label != NULL; p++) {
|
||||
if (pcf_add_value(t, p->value, p->label) == NULL) {
|
||||
if (pcf_add_value(t, (int) p->value, p->label) == NULL) {
|
||||
err("pcf_add_value failed");
|
||||
return -1;
|
||||
}
|
||||
@ -66,7 +66,7 @@ create_type(const struct model_pvt_spec *pvt,
|
||||
return -1;
|
||||
}
|
||||
|
||||
struct pcf_type *pcftype = pcf_add_type(pcf, type, label);
|
||||
struct pcf_type *pcftype = pcf_add_type(pcf, (int) type, label);
|
||||
if (pcftype == NULL) {
|
||||
err("pcf_add_type failed");
|
||||
return -1;
|
||||
@ -106,7 +106,7 @@ connect_cpu_prv(struct emu *emu, struct cpu *scpu, struct prv *prv, int id)
|
||||
for (int i = 0; i < spec->nch; i++) {
|
||||
struct chan *out = track_get_output(&cpu->track[i]);
|
||||
long type = spec->pvt->type[i];
|
||||
long row = scpu->gindex;
|
||||
long row = (long) scpu->gindex;
|
||||
long flags = flags_arr ? flags_arr[i] : 0;
|
||||
if (prv_register(prv, row, type, &emu->bay, out, flags)) {
|
||||
err("prv_register failed");
|
||||
@ -158,7 +158,7 @@ connect_thread_prv(struct emu *emu, struct thread *sth, struct prv *prv, int id)
|
||||
for (int i = 0; i < spec->nch; i++) {
|
||||
struct chan *out = track_get_output(&th->track[i]);
|
||||
long type = spec->pvt->type[i];
|
||||
long row = sth->gindex;
|
||||
long row = (long) sth->gindex;
|
||||
long flags = flags_arr ? flags_arr[i] : 0;
|
||||
if (prv_register(prv, row, type, &emu->bay, out, flags)) {
|
||||
err("prv_register failed");
|
||||
@ -178,7 +178,7 @@ model_pvt_connect_thread(struct emu *emu, const struct model_thread_spec *spec)
|
||||
/* Get cpu PRV */
|
||||
struct pvt *pvt = recorder_find_pvt(&emu->recorder, "thread");
|
||||
if (pvt == NULL) {
|
||||
err("cannot find cpu pvt");
|
||||
err("cannot find thread pvt");
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
@ -1,4 +1,4 @@
|
||||
/* Copyright (c) 2021-2023 Barcelona Supercomputing Center (BSC)
|
||||
/* Copyright (c) 2021-2024 Barcelona Supercomputing Center (BSC)
|
||||
* SPDX-License-Identifier: GPL-3.0-or-later */
|
||||
|
||||
#include "model_thread.h"
|
||||
@ -19,10 +19,10 @@
|
||||
static int
|
||||
init_chan(struct model_thread *th, const struct model_chan_spec *spec, int64_t gindex)
|
||||
{
|
||||
const char *fmt = "%s.thread%ld.%s";
|
||||
const char *fmt = "%s.thread%"PRIi64".%s";
|
||||
const char *prefix = spec->prefix;
|
||||
|
||||
th->ch = calloc(spec->nch, sizeof(struct chan));
|
||||
th->ch = calloc((size_t) spec->nch, sizeof(struct chan));
|
||||
if (th->ch == NULL) {
|
||||
err("calloc failed:");
|
||||
return -1;
|
||||
@ -30,7 +30,7 @@ init_chan(struct model_thread *th, const struct model_chan_spec *spec, int64_t g
|
||||
|
||||
for (int i = 0; i < spec->nch; i++) {
|
||||
struct chan *c = &th->ch[i];
|
||||
int type = spec->ch_stack[i];
|
||||
enum chan_type type = spec->ch_stack[i] ? CHAN_STACK : CHAN_SINGLE;
|
||||
const char *ch_name = spec->ch_names[i];
|
||||
chan_init(c, type, fmt, prefix, gindex, ch_name);
|
||||
|
||||
@ -45,7 +45,7 @@ init_chan(struct model_thread *th, const struct model_chan_spec *spec, int64_t g
|
||||
}
|
||||
}
|
||||
|
||||
th->track = calloc(spec->nch, sizeof(struct track));
|
||||
th->track = calloc((size_t) spec->nch, sizeof(struct track));
|
||||
if (th->track == NULL) {
|
||||
err("calloc failed:");
|
||||
return -1;
|
||||
|
@ -1,4 +1,4 @@
|
||||
/* Copyright (c) 2021-2023 Barcelona Supercomputing Center (BSC)
|
||||
/* Copyright (c) 2021-2024 Barcelona Supercomputing Center (BSC)
|
||||
* SPDX-License-Identifier: GPL-3.0-or-later */
|
||||
|
||||
#include "mux.h"
|
||||
@ -25,7 +25,7 @@ default_select(struct mux *mux,
|
||||
int64_t index = key.i;
|
||||
|
||||
if (index < 0 || index >= mux->ninputs) {
|
||||
err("index out of bounds %ld", index);
|
||||
err("index out of bounds %"PRIi64, index);
|
||||
return -1;
|
||||
}
|
||||
|
||||
@ -177,7 +177,7 @@ mux_init(struct mux *mux,
|
||||
mux->select = select;
|
||||
mux->output = output;
|
||||
mux->ninputs = ninputs;
|
||||
mux->inputs = calloc(ninputs, sizeof(struct mux_input));
|
||||
mux->inputs = calloc((size_t) ninputs, sizeof(struct mux_input));
|
||||
mux->def = value_null();
|
||||
|
||||
if (mux->inputs == NULL) {
|
||||
@ -214,7 +214,7 @@ mux_set_input(struct mux *mux, int64_t index, struct chan *chan)
|
||||
struct mux_input *input = &mux->inputs[index];
|
||||
|
||||
if (input->chan != NULL) {
|
||||
err("input %d already has a channel", index);
|
||||
err("input %"PRIi64" already has a channel", index);
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
@ -1,4 +1,4 @@
|
||||
/* Copyright (c) 2023 Barcelona Supercomputing Center (BSC)
|
||||
/* Copyright (c) 2023-2024 Barcelona Supercomputing Center (BSC)
|
||||
* SPDX-License-Identifier: GPL-3.0-or-later */
|
||||
|
||||
#include "breakdown.h"
|
||||
@ -32,8 +32,8 @@ static int
|
||||
create_cpu(struct bay *bay, struct breakdown_cpu *bcpu, int64_t gindex)
|
||||
{
|
||||
enum chan_type t = CHAN_SINGLE;
|
||||
chan_init(&bcpu->tr, t, "nanos6.cpu%ld.breakdown.tr", gindex);
|
||||
chan_init(&bcpu->tri, t, "nanos6.cpu%ld.breakdown.tri", gindex);
|
||||
chan_init(&bcpu->tr, t, "nanos6.cpu%"PRIi64".breakdown.tr", gindex);
|
||||
chan_init(&bcpu->tri, t, "nanos6.cpu%"PRIi64".breakdown.tri", gindex);
|
||||
|
||||
/* Register all channels in the bay */
|
||||
if (bay_register(bay, &bcpu->tr) != 0) {
|
||||
@ -59,12 +59,12 @@ model_nanos6_breakdown_create(struct emu *emu)
|
||||
|
||||
/* Count phy cpus */
|
||||
struct system *sys = &emu->system;
|
||||
int64_t nphycpus = sys->ncpus - sys->nlooms;
|
||||
int64_t nphycpus = (int64_t) (sys->ncpus - sys->nlooms);
|
||||
bemu->nphycpus = nphycpus;
|
||||
|
||||
/* Create a new Paraver trace */
|
||||
struct recorder *rec = &emu->recorder;
|
||||
bemu->pvt = recorder_add_pvt(rec, "nanos6-breakdown", nphycpus);
|
||||
bemu->pvt = recorder_add_pvt(rec, "nanos6-breakdown", (long) nphycpus);
|
||||
if (bemu->pvt == NULL) {
|
||||
err("recorder_add_pvt failed");
|
||||
return -1;
|
||||
@ -131,7 +131,7 @@ select_tr(struct mux *mux, struct value value, struct mux_input **input)
|
||||
|
||||
int64_t i = in_body;
|
||||
char *inputs[] = { "subsystem", "task_type" };
|
||||
dbg("selecting input %ld (%s)", i, inputs[i]);
|
||||
dbg("selecting input %"PRIi64" (%s)", i, inputs[i]);
|
||||
*input = mux_get_input(mux, i);
|
||||
|
||||
return 0;
|
||||
@ -239,8 +239,13 @@ model_nanos6_breakdown_connect(struct emu *emu)
|
||||
long type = PRV_NANOS6_BREAKDOWN;
|
||||
long flags = PRV_SKIPDUP;
|
||||
|
||||
/* We may emit zero at the start, when an input changes and all
|
||||
* the other sort output channels write a zero in the output,
|
||||
* before the last value is set in prv.c. */
|
||||
flags |= PRV_ZERO;
|
||||
|
||||
struct chan *out = sort_get_output(&bemu->sort, i);
|
||||
if (prv_register(prv, i, type, bay, out, flags)) {
|
||||
if (prv_register(prv, (long) i, type, bay, out, flags)) {
|
||||
err("prv_register failed");
|
||||
return -1;
|
||||
}
|
||||
@ -263,7 +268,7 @@ model_nanos6_breakdown_finish(struct emu *emu,
|
||||
struct pcf *pcf = pvt_get_pcf(bemu->pvt);
|
||||
long typeid = PRV_NANOS6_BREAKDOWN;
|
||||
char label[] = "CPU: Nanos6 Runtime/Idle/Task breakdown";
|
||||
struct pcf_type *pcftype = pcf_add_type(pcf, typeid, label);
|
||||
struct pcf_type *pcftype = pcf_add_type(pcf, (int) typeid, label);
|
||||
const struct pcf_value_label *v = NULL;
|
||||
|
||||
/* Emit subsystem values */
|
||||
@ -297,12 +302,12 @@ model_nanos6_breakdown_finish(struct emu *emu,
|
||||
struct prf *prf = pvt_get_prf(bemu->pvt);
|
||||
for (int64_t row = 0; row < bemu->nphycpus; row++) {
|
||||
char name[128];
|
||||
if (snprintf(name, 128, "~CPU %4ld", bemu->nphycpus - row) >= 128) {
|
||||
if (snprintf(name, 128, "~CPU %4" PRIi64, bemu->nphycpus - row) >= 128) {
|
||||
err("label too long");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (prf_add(prf, row, name) != 0) {
|
||||
if (prf_add(prf, (long) row, name) != 0) {
|
||||
err("prf_add failed for %s", name);
|
||||
return -1;
|
||||
}
|
||||
|
@ -1,4 +1,4 @@
|
||||
/* Copyright (c) 2021-2023 Barcelona Supercomputing Center (BSC)
|
||||
/* Copyright (c) 2021-2024 Barcelona Supercomputing Center (BSC)
|
||||
* SPDX-License-Identifier: GPL-3.0-or-later */
|
||||
|
||||
#include "nanos6_priv.h"
|
||||
@ -299,7 +299,7 @@ update_task_state(struct emu *emu)
|
||||
static int
|
||||
expand_transition_value(struct emu *emu, int was_running, int runs_now, char *tr_p)
|
||||
{
|
||||
char tr = emu->ev->v;
|
||||
char tr = (char) emu->ev->v;
|
||||
|
||||
/* Ensure we don't clobber the value */
|
||||
if (tr == 'X' || tr == 'E') {
|
||||
@ -419,7 +419,7 @@ update_task(struct emu *emu)
|
||||
struct task *next = bnext == NULL ? NULL : body_get_task(bnext);
|
||||
|
||||
/* Update the subsystem channel */
|
||||
if (update_task_ss_channel(emu, emu->ev->v) != 0) {
|
||||
if (update_task_ss_channel(emu, (char) emu->ev->v) != 0) {
|
||||
err("update_task_ss_channel failed");
|
||||
return -1;
|
||||
}
|
||||
@ -467,7 +467,7 @@ create_task(struct emu *emu)
|
||||
* task, so we relax the model to allow this for now. */
|
||||
flags |= TASK_FLAG_RELAX_NESTING;
|
||||
|
||||
if (task_create(info, type_id, task_id, flags) != 0) {
|
||||
if (task_create(info, type_id, task_id, (uint32_t) flags) != 0) {
|
||||
err("task_create failed");
|
||||
return -1;
|
||||
}
|
||||
@ -521,7 +521,8 @@ pre_type(struct emu *emu)
|
||||
}
|
||||
|
||||
const uint8_t *data = &emu->ev->payload->jumbo.data[0];
|
||||
uint32_t typeid = *(uint32_t *) data;
|
||||
uint32_t typeid;
|
||||
memcpy(&typeid, data, 4); /* May be unaligned */
|
||||
data += 4;
|
||||
|
||||
const char *label = (const char *) data;
|
||||
|
@ -402,7 +402,7 @@ finish_pvt(struct emu *emu, const char *name)
|
||||
}
|
||||
struct pcf *pcf = pvt_get_pcf(pvt);
|
||||
long typeid = pvt_type[CH_TYPE];
|
||||
struct pcf_type *pcftype = pcf_find_type(pcf, typeid);
|
||||
struct pcf_type *pcftype = pcf_find_type(pcf, (int) typeid);
|
||||
|
||||
for (struct proc *p = sys->procs; p; p = p->gnext) {
|
||||
struct nanos6_proc *proc = EXT(p, model_id);
|
||||
|
342
src/emu/nosv/breakdown.c
Normal file
342
src/emu/nosv/breakdown.c
Normal file
@ -0,0 +1,342 @@
|
||||
/* Copyright (c) 2023-2024 Barcelona Supercomputing Center (BSC)
|
||||
* SPDX-License-Identifier: GPL-3.0-or-later */
|
||||
|
||||
#include "breakdown.h"
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include "bay.h"
|
||||
#include "chan.h"
|
||||
#include "common.h"
|
||||
#include "cpu.h"
|
||||
#include "emu.h"
|
||||
#include "emu_args.h"
|
||||
#include "emu_prv.h"
|
||||
#include "extend.h"
|
||||
#include "model_cpu.h"
|
||||
#include "mux.h"
|
||||
#include "nosv_priv.h"
|
||||
#include "proc.h"
|
||||
#include "pv/pcf.h"
|
||||
#include "pv/prf.h"
|
||||
#include "pv/prv.h"
|
||||
#include "pv/pvt.h"
|
||||
#include "recorder.h"
|
||||
#include "sort.h"
|
||||
#include "system.h"
|
||||
#include "thread.h"
|
||||
#include "task.h"
|
||||
#include "track.h"
|
||||
#include "value.h"
|
||||
|
||||
|
||||
static int
|
||||
create_cpu(struct bay *bay, struct nosv_breakdown_cpu *bcpu, int64_t gindex)
|
||||
{
|
||||
enum chan_type t = CHAN_SINGLE;
|
||||
chan_init(&bcpu->tr, t, "nosv.cpu%"PRIi64".breakdown.tr", gindex);
|
||||
chan_init(&bcpu->tri, t, "nosv.cpu%"PRIi64".breakdown.tri", gindex);
|
||||
|
||||
/* Register all channels in the bay */
|
||||
if (bay_register(bay, &bcpu->tr) != 0) {
|
||||
err("bay_register tr failed");
|
||||
return -1;
|
||||
}
|
||||
if (bay_register(bay, &bcpu->tri) != 0) {
|
||||
err("bay_register tri failed");
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
check_thread_metadata(struct thread *th)
|
||||
{
|
||||
if (th->meta == NULL) {
|
||||
err("thread has no metadata");
|
||||
return -1;
|
||||
}
|
||||
|
||||
JSON_Value *val = json_object_dotget_value(th->meta, "nosv.can_breakdown");
|
||||
if (val == NULL) {
|
||||
err("missing nosv.can_breakdown attribute");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (!json_value_get_boolean(val)) {
|
||||
err("nosv.can_breakdown is false, missing events to enable breakdown");
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
model_nosv_breakdown_create(struct emu *emu)
|
||||
{
|
||||
if (emu->args.breakdown == 0)
|
||||
return 0;
|
||||
|
||||
struct nosv_emu *memu = EXT(emu, 'V');
|
||||
struct nosv_breakdown_emu *bemu = &memu->breakdown;
|
||||
|
||||
/* Count phy cpus */
|
||||
struct system *sys = &emu->system;
|
||||
int64_t nphycpus = (int64_t) (sys->ncpus - sys->nlooms);
|
||||
bemu->nphycpus = nphycpus;
|
||||
|
||||
/* Create a new Paraver trace */
|
||||
struct recorder *rec = &emu->recorder;
|
||||
bemu->pvt = recorder_add_pvt(rec, "nosv-breakdown", (long) nphycpus);
|
||||
if (bemu->pvt == NULL) {
|
||||
err("recorder_add_pvt failed");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (sort_init(&bemu->sort, &emu->bay, nphycpus, "nosv.breakdown.sort") != 0) {
|
||||
err("sort_init failed");
|
||||
return -1;
|
||||
}
|
||||
|
||||
for (struct cpu *cpu = sys->cpus; cpu; cpu = cpu->next) {
|
||||
if (cpu->is_virtual)
|
||||
continue;
|
||||
|
||||
struct nosv_cpu *mcpu = EXT(cpu, 'V');
|
||||
struct nosv_breakdown_cpu *bcpu = &mcpu->breakdown;
|
||||
|
||||
if (create_cpu(&emu->bay, bcpu, cpu->gindex) != 0) {
|
||||
err("create_cpu failed");
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
for (struct thread *th = emu->system.threads; th; th = th->gnext) {
|
||||
if (check_thread_metadata(th) != 0) {
|
||||
err("bad nosv metadata in thread: %s", th->id);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
select_tr(struct mux *mux, struct value value, struct mux_input **input)
|
||||
{
|
||||
/* Only select the task if we are in ST_TASK_BODY and the task_type has
|
||||
* a non-null value */
|
||||
|
||||
int64_t in_body = (value.type == VALUE_INT64 && value.i == ST_TASK_BODY);
|
||||
|
||||
if (in_body) {
|
||||
struct value tt;
|
||||
struct mux_input *ttinput = mux_get_input(mux, 1);
|
||||
if (chan_read(ttinput->chan, &tt) != 0) {
|
||||
err("chan_read failed");
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Only show task type if we have a task */
|
||||
if (tt.type == VALUE_NULL)
|
||||
in_body = 0;
|
||||
}
|
||||
|
||||
if (!in_body) {
|
||||
/* Only select ss if not NULL */
|
||||
struct value ss;
|
||||
struct mux_input *ssinput = mux_get_input(mux, 0);
|
||||
if (chan_read(ssinput->chan, &ss) != 0) {
|
||||
err("chan_read failed");
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Don't select anything, so the default output is shown */
|
||||
if (ss.type == VALUE_NULL) {
|
||||
dbg("not selecting anything");
|
||||
*input = NULL;
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
int64_t i = in_body;
|
||||
char *inputs[] = { "subsystem", "task_type" };
|
||||
dbg("selecting input %"PRIi64" (%s)", i, inputs[i]);
|
||||
*input = mux_get_input(mux, i);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
select_idle(struct mux *mux, struct value value, struct mux_input **input)
|
||||
{
|
||||
dbg("selecting tri output for value %s", value_str(value));
|
||||
|
||||
if (value.type == VALUE_INT64 && value.i == ST_PROGRESSING) {
|
||||
dbg("selecting input 0 (tr)");
|
||||
*input = mux_get_input(mux, 0);
|
||||
} else {
|
||||
dbg("selecting input 1 (idle)");
|
||||
*input = mux_get_input(mux, 1);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
connect_cpu(struct bay *bay, struct nosv_cpu *mcpu)
|
||||
{
|
||||
struct nosv_breakdown_cpu *bcpu = &mcpu->breakdown;
|
||||
|
||||
/* Channel aliases */
|
||||
struct chan *ss = &mcpu->m.track[CH_SUBSYSTEM].ch;
|
||||
struct chan *tt = &mcpu->m.track[CH_TYPE].ch;
|
||||
struct chan *idle = &mcpu->m.track[CH_IDLE].ch;
|
||||
struct chan *tr = &bcpu->tr;
|
||||
struct chan *tri = &bcpu->tri;
|
||||
|
||||
/* Connect mux0 using ss as select */
|
||||
if (mux_init(&bcpu->mux0, bay, ss, tr, select_tr, 2) != 0) {
|
||||
err("mux_init failed");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (mux_set_input(&bcpu->mux0, 0, ss) != 0) {
|
||||
err("mux_set_input ss failed");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (mux_set_input(&bcpu->mux0, 1, tt) != 0) {
|
||||
err("mux_set_input tt failed");
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Emit unknown subsystem on NULL */
|
||||
mux_set_default(&bcpu->mux0, value_int64(ST_UNKNOWN_SS));
|
||||
|
||||
/* Connect mux 1 using idle as select */
|
||||
if (mux_init(&bcpu->mux1, bay, idle, tri, select_idle, 2) != 0) {
|
||||
err("mux_init failed");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (mux_set_input(&bcpu->mux1, 0, tr) != 0) {
|
||||
err("mux_set_input tr failed");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (mux_set_input(&bcpu->mux1, 1, idle) != 0) {
|
||||
err("mux_set_input idle failed");
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
model_nosv_breakdown_connect(struct emu *emu)
|
||||
{
|
||||
if (emu->args.breakdown == 0)
|
||||
return 0;
|
||||
|
||||
struct nosv_emu *memu = EXT(emu, 'V');
|
||||
struct nosv_breakdown_emu *bemu = &memu->breakdown;
|
||||
struct bay *bay = &emu->bay;
|
||||
struct system *sys = &emu->system;
|
||||
|
||||
int64_t i = 0;
|
||||
for (struct cpu *cpu = sys->cpus; cpu; cpu = cpu->next) {
|
||||
if (cpu->is_virtual)
|
||||
continue;
|
||||
|
||||
struct nosv_cpu *mcpu = EXT(cpu, 'V');
|
||||
struct nosv_breakdown_cpu *bcpu = &mcpu->breakdown;
|
||||
|
||||
/* Connect tr and tri channels and muxes */
|
||||
if (connect_cpu(bay, mcpu) != 0) {
|
||||
err("connect_cpu failed");
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Connect tri to sort */
|
||||
if (sort_set_input(&bemu->sort, i, &bcpu->tri) != 0) {
|
||||
err("sort_set_input failed");
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Connect out to PRV */
|
||||
struct prv *prv = pvt_get_prv(bemu->pvt);
|
||||
long type = PRV_NOSV_BREAKDOWN;
|
||||
long flags = PRV_SKIPDUP | PRV_ZERO;
|
||||
|
||||
struct chan *out = sort_get_output(&bemu->sort, i);
|
||||
if (prv_register(prv, (long) i, type, bay, out, flags)) {
|
||||
err("prv_register failed");
|
||||
return -1;
|
||||
}
|
||||
|
||||
i++;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
model_nosv_breakdown_finish(struct emu *emu,
|
||||
const struct pcf_value_label **labels)
|
||||
{
|
||||
if (emu->args.breakdown == 0)
|
||||
return 0;
|
||||
|
||||
struct nosv_emu *memu = EXT(emu, 'V');
|
||||
struct nosv_breakdown_emu *bemu = &memu->breakdown;
|
||||
struct pcf *pcf = pvt_get_pcf(bemu->pvt);
|
||||
long typeid = PRV_NOSV_BREAKDOWN;
|
||||
char label[] = "CPU: nOS-V Runtime/Idle/Task breakdown";
|
||||
struct pcf_type *pcftype = pcf_add_type(pcf, (int) typeid, label);
|
||||
const struct pcf_value_label *v = NULL;
|
||||
|
||||
/* Emit subsystem values */
|
||||
for (v = labels[CH_SUBSYSTEM]; v->label; v++) {
|
||||
if (pcf_add_value(pcftype, v->value, v->label) == NULL) {
|
||||
err("pcf_add_value ss failed");
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
/* Emit idle values */
|
||||
for (v = labels[CH_IDLE]; v->label; v++) {
|
||||
if (pcf_add_value(pcftype, v->value, v->label) == NULL) {
|
||||
err("pcf_add_value idle failed");
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
/* Emit task_type values */
|
||||
struct system *sys = &emu->system;
|
||||
for (struct proc *p = sys->procs; p; p = p->gnext) {
|
||||
struct nosv_proc *proc = EXT(p, 'V');
|
||||
struct task_info *info = &proc->task_info;
|
||||
if (task_create_pcf_types(pcftype, info->types) != 0) {
|
||||
err("task_create_pcf_types failed");
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
/* Also populate the row labels */
|
||||
struct prf *prf = pvt_get_prf(bemu->pvt);
|
||||
for (int64_t row = 0; row < bemu->nphycpus; row++) {
|
||||
char name[128];
|
||||
if (snprintf(name, 128, "~CPU %4"PRIi64, bemu->nphycpus - row) >= 128) {
|
||||
err("label too long");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (prf_add(prf, (long) row, name) != 0) {
|
||||
err("prf_add failed for %s", name);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
56
src/emu/nosv/breakdown.h
Normal file
56
src/emu/nosv/breakdown.h
Normal file
@ -0,0 +1,56 @@
|
||||
/* Copyright (c) 2023 Barcelona Supercomputing Center (BSC)
|
||||
* SPDX-License-Identifier: GPL-3.0-or-later */
|
||||
|
||||
#ifndef BREAKDOWN_H
|
||||
#define BREAKDOWN_H
|
||||
|
||||
/*
|
||||
* The breakdown model is implemented on top of the CPU subsystem, task_type and
|
||||
* idle channels. The first mux0 selects the task type when the subsystem
|
||||
* matches "Task body" otherwise forwards the subsystem as-is to tr. The second
|
||||
* mux1 selects tr only when the CPU is not Idle, otherwise sets the output tri
|
||||
* as Idle.
|
||||
*
|
||||
* +--------+
|
||||
* | |
|
||||
* | v
|
||||
* | +------+
|
||||
* subsystem -+-->--| |
|
||||
* | mux0 | +------+
|
||||
* task_type ---->--| |-->-- tr -->--| |
|
||||
* +------+ | mux1 |-->-- tri
|
||||
* idle --------->-------------------+->--| |
|
||||
* | +------+
|
||||
* | ^
|
||||
* | |
|
||||
* +--------+
|
||||
*
|
||||
* Then the sort module takes the output tri of each CPU and sorts the values
|
||||
* which are propagated to the PRV directly.
|
||||
*
|
||||
* +------+ +-----+
|
||||
* cpu0.tri --->---| |--->---| |
|
||||
* ... | sort | ... | PRV |
|
||||
* cpuN.tri --->---| |--->---| |
|
||||
* +------+ +-----+
|
||||
*/
|
||||
|
||||
#include <stdint.h>
|
||||
#include "chan.h"
|
||||
#include "mux.h"
|
||||
#include "sort.h"
|
||||
|
||||
struct nosv_breakdown_cpu {
|
||||
struct mux mux0;
|
||||
struct chan tr;
|
||||
struct mux mux1;
|
||||
struct chan tri;
|
||||
};
|
||||
|
||||
struct nosv_breakdown_emu {
|
||||
int64_t nphycpus;
|
||||
struct sort sort;
|
||||
struct pvt *pvt;
|
||||
};
|
||||
|
||||
#endif /* BREAKDOWN_H */
|
@ -16,7 +16,7 @@
|
||||
#include "thread.h"
|
||||
#include "value.h"
|
||||
|
||||
enum { PUSH = 1, POP = 2, IGN = 3 };
|
||||
enum { PUSH = 1, POP = 2, SET = 3, IGN = 4 };
|
||||
|
||||
#define CHSS CH_SUBSYSTEM
|
||||
|
||||
@ -67,6 +67,12 @@ static const int ss_table[256][256][3] = {
|
||||
['U'] = { CHSS, POP, ST_API_MUTEX_UNLOCK },
|
||||
['b'] = { CHSS, PUSH, ST_API_BARRIER_WAIT },
|
||||
['B'] = { CHSS, POP, ST_API_BARRIER_WAIT },
|
||||
['o'] = { CHSS, PUSH, ST_API_COND_WAIT },
|
||||
['O'] = { CHSS, POP, ST_API_COND_WAIT },
|
||||
['g'] = { CHSS, PUSH, ST_API_COND_SIGNAL },
|
||||
['G'] = { CHSS, POP, ST_API_COND_SIGNAL },
|
||||
['k'] = { CHSS, PUSH, ST_API_COND_BCAST },
|
||||
['K'] = { CHSS, POP, ST_API_COND_BCAST },
|
||||
},
|
||||
/* FIXME: Move thread type to another channel, like nanos6 */
|
||||
['H'] = {
|
||||
@ -77,6 +83,11 @@ static const int ss_table[256][256][3] = {
|
||||
['d'] = { CHSS, PUSH, ST_DELEGATE },
|
||||
['D'] = { CHSS, POP, ST_DELEGATE },
|
||||
},
|
||||
['P'] = {
|
||||
['p'] = { CH_IDLE, SET, ST_PROGRESSING },
|
||||
['r'] = { CH_IDLE, SET, ST_RESTING },
|
||||
['a'] = { CH_IDLE, SET, ST_ABSORBING },
|
||||
},
|
||||
};
|
||||
|
||||
static int
|
||||
@ -94,6 +105,8 @@ simple(struct emu *emu)
|
||||
return chan_push(ch, value_int64(st));
|
||||
} else if (action == POP) {
|
||||
return chan_pop(ch, value_int64(st));
|
||||
} else if (action == SET) {
|
||||
return chan_set(ch, value_int64(st));
|
||||
} else if (action == IGN) {
|
||||
return 0; /* do nothing */
|
||||
} else {
|
||||
@ -317,7 +330,7 @@ update_task_state(struct emu *emu)
|
||||
static int
|
||||
expand_transition_value(struct emu *emu, int was_running, int runs_now, char *tr_p)
|
||||
{
|
||||
char tr = emu->ev->v;
|
||||
char tr = (char) emu->ev->v;
|
||||
|
||||
/* Ensure we don't clobber the value */
|
||||
if (tr == 'X' || tr == 'E') {
|
||||
@ -434,7 +447,7 @@ update_task(struct emu *emu)
|
||||
struct body *next = task_get_running(stack);
|
||||
|
||||
/* Update the subsystem channel */
|
||||
if (update_task_ss_channel(emu, emu->ev->v) != 0) {
|
||||
if (update_task_ss_channel(emu, (char) emu->ev->v) != 0) {
|
||||
err("update_task_ss_channel failed");
|
||||
return -1;
|
||||
}
|
||||
@ -502,7 +515,7 @@ pre_task(struct emu *emu)
|
||||
switch (emu->ev->v) {
|
||||
case 'C':
|
||||
case 'c':
|
||||
ret = create_task(emu, emu->ev->v);
|
||||
ret = create_task(emu, (char) emu->ev->v);
|
||||
break;
|
||||
case 'x':
|
||||
case 'e':
|
||||
@ -539,7 +552,8 @@ pre_type(struct emu *emu)
|
||||
}
|
||||
|
||||
const uint8_t *data = &emu->ev->payload->jumbo.data[0];
|
||||
uint32_t typeid = *(uint32_t *) data;
|
||||
uint32_t typeid;
|
||||
memcpy(&typeid, data, 4); /* May be unaligned */
|
||||
data += 4;
|
||||
|
||||
const char *label = (const char *) data;
|
||||
@ -574,6 +588,7 @@ process_ev(struct emu *emu)
|
||||
case 'M':
|
||||
case 'H':
|
||||
case 'A':
|
||||
case 'P':
|
||||
return simple(emu);
|
||||
case 'T':
|
||||
return pre_task(emu);
|
||||
|
@ -4,6 +4,7 @@
|
||||
#ifndef NOSV_PRIV_H
|
||||
#define NOSV_PRIV_H
|
||||
|
||||
#include "breakdown.h"
|
||||
#include "emu.h"
|
||||
#include "task.h"
|
||||
#include "model_cpu.h"
|
||||
@ -18,10 +19,12 @@ enum nosv_chan {
|
||||
CH_APPID,
|
||||
CH_SUBSYSTEM,
|
||||
CH_RANK,
|
||||
CH_IDLE,
|
||||
CH_MAX,
|
||||
};
|
||||
|
||||
enum nosv_ss_values {
|
||||
ST_UNKNOWN_SS = 2,
|
||||
ST_SCHED_HUNGRY = 6,
|
||||
ST_SCHED_SERVING,
|
||||
ST_SCHED_SUBMITTING,
|
||||
@ -41,6 +44,9 @@ enum nosv_ss_values {
|
||||
ST_API_MUTEX_TRYLOCK,
|
||||
ST_API_MUTEX_UNLOCK,
|
||||
ST_API_BARRIER_WAIT,
|
||||
ST_API_COND_WAIT,
|
||||
ST_API_COND_SIGNAL,
|
||||
ST_API_COND_BCAST,
|
||||
ST_WORKER,
|
||||
ST_DELEGATE,
|
||||
|
||||
@ -56,16 +62,36 @@ struct nosv_thread {
|
||||
|
||||
struct nosv_cpu {
|
||||
struct model_cpu m;
|
||||
struct nosv_breakdown_cpu breakdown;
|
||||
};
|
||||
|
||||
struct nosv_proc {
|
||||
struct task_info task_info;
|
||||
};
|
||||
|
||||
struct nosv_emu {
|
||||
int connected;
|
||||
int event;
|
||||
struct nosv_breakdown_emu breakdown;
|
||||
};
|
||||
|
||||
enum nosv_progress {
|
||||
/* Can mix with subsystem values */
|
||||
ST_PROGRESSING = 100,
|
||||
ST_RESTING,
|
||||
ST_ABSORBING,
|
||||
};
|
||||
|
||||
int model_nosv_probe(struct emu *emu);
|
||||
int model_nosv_create(struct emu *emu);
|
||||
int model_nosv_connect(struct emu *emu);
|
||||
int model_nosv_event(struct emu *emu);
|
||||
int model_nosv_finish(struct emu *emu);
|
||||
|
||||
int model_nosv_breakdown_create(struct emu *emu);
|
||||
int model_nosv_breakdown_connect(struct emu *emu);
|
||||
int model_nosv_breakdown_finish(struct emu *emu,
|
||||
const struct pcf_value_label **labels);
|
||||
|
||||
|
||||
#endif /* NOSV_PRIV_H */
|
||||
|
@ -5,6 +5,7 @@
|
||||
#include <stddef.h>
|
||||
#include <stdlib.h>
|
||||
#include "chan.h"
|
||||
#include "cpu.h"
|
||||
#include "common.h"
|
||||
#include "emu.h"
|
||||
#include "emu_args.h"
|
||||
@ -64,6 +65,9 @@ static struct ev_decl model_evlist[] = {
|
||||
PAIR_E("VAt", "VAT", "nosv_mutex_trylock()")
|
||||
PAIR_E("VAu", "VAU", "nosv_mutex_unlock()")
|
||||
PAIR_E("VAb", "VAB", "nosv_barrier_wait()")
|
||||
PAIR_E("VAo", "VAO", "nosv_cond_wait()")
|
||||
PAIR_E("VAg", "VAG", "nosv_cond_signal()")
|
||||
PAIR_E("VAk", "VAK", "nosv_cond_broadcast()")
|
||||
|
||||
/* FIXME: VHA and VHa are not subsystems */
|
||||
{ "VHa", "enters nosv_attach()" },
|
||||
@ -72,12 +76,16 @@ static struct ev_decl model_evlist[] = {
|
||||
PAIR_B("VHw", "VHW", "execution as worker")
|
||||
PAIR_B("VHd", "VHD", "execution as delegate")
|
||||
|
||||
{ "VPp", "sets progress state to Progressing" },
|
||||
{ "VPr", "sets progress state to Resting" },
|
||||
{ "VPa", "sets progress state to Absorbing" },
|
||||
|
||||
{ NULL, NULL },
|
||||
};
|
||||
|
||||
struct model_spec model_nosv = {
|
||||
.name = model_name,
|
||||
.version = "2.1.0",
|
||||
.version = "2.4.0",
|
||||
.evlist = model_evlist,
|
||||
.model = model_id,
|
||||
.create = model_nosv_create,
|
||||
@ -96,6 +104,7 @@ static const char *chan_name[CH_MAX] = {
|
||||
[CH_APPID] = "appid",
|
||||
[CH_SUBSYSTEM] = "subsystem",
|
||||
[CH_RANK] = "rank",
|
||||
[CH_IDLE] = "idle",
|
||||
};
|
||||
|
||||
static const int chan_stack[CH_MAX] = {
|
||||
@ -121,6 +130,7 @@ static const int pvt_type[CH_MAX] = {
|
||||
[CH_APPID] = PRV_NOSV_APPID,
|
||||
[CH_SUBSYSTEM] = PRV_NOSV_SUBSYSTEM,
|
||||
[CH_RANK] = PRV_NOSV_RANK,
|
||||
[CH_IDLE] = PRV_NOSV_IDLE,
|
||||
};
|
||||
|
||||
static const char *pcf_prefix[CH_MAX] = {
|
||||
@ -130,9 +140,11 @@ static const char *pcf_prefix[CH_MAX] = {
|
||||
[CH_APPID] = "nOS-V task AppID",
|
||||
[CH_SUBSYSTEM] = "nOS-V subsystem",
|
||||
[CH_RANK] = "nOS-V task MPI rank",
|
||||
[CH_IDLE] = "nOS-V idle state",
|
||||
};
|
||||
|
||||
static const struct pcf_value_label nosv_ss_values[] = {
|
||||
{ ST_UNKNOWN_SS, "Unknown subsystem" },
|
||||
{ ST_SCHED_HUNGRY, "Scheduler: Hungry" },
|
||||
{ ST_SCHED_SERVING, "Scheduler: Serving" },
|
||||
{ ST_SCHED_SUBMITTING, "Scheduler: Submitting" },
|
||||
@ -152,6 +164,9 @@ static const struct pcf_value_label nosv_ss_values[] = {
|
||||
{ ST_API_MUTEX_TRYLOCK,"API: Mutex trylock" },
|
||||
{ ST_API_MUTEX_UNLOCK, "API: Mutex unlock" },
|
||||
{ ST_API_BARRIER_WAIT, "API: Barrier wait" },
|
||||
{ ST_API_COND_WAIT, "API: Cond wait" },
|
||||
{ ST_API_COND_SIGNAL, "API: Cond signal" },
|
||||
{ ST_API_COND_BCAST, "API: Cond broadcast" },
|
||||
{ ST_WORKER, "Thread: Worker" },
|
||||
{ ST_DELEGATE, "Thread: Delegate" },
|
||||
{ EV_SCHED_SEND, "EV Scheduler: Send task" },
|
||||
@ -160,8 +175,16 @@ static const struct pcf_value_label nosv_ss_values[] = {
|
||||
{ -1, NULL },
|
||||
};
|
||||
|
||||
static const struct pcf_value_label nosv_worker_idle[] = {
|
||||
{ ST_PROGRESSING, "Progressing" },
|
||||
{ ST_RESTING, "Resting" },
|
||||
{ ST_ABSORBING, "Absorbing noise" },
|
||||
{ -1, NULL },
|
||||
};
|
||||
|
||||
static const struct pcf_value_label *pcf_labels[CH_MAX] = {
|
||||
[CH_SUBSYSTEM] = nosv_ss_values,
|
||||
[CH_IDLE] = nosv_worker_idle,
|
||||
};
|
||||
|
||||
static const long prv_flags[CH_MAX] = {
|
||||
@ -171,6 +194,7 @@ static const long prv_flags[CH_MAX] = {
|
||||
[CH_APPID] = PRV_SKIPDUPNULL, /* Switch to task of same appid */
|
||||
[CH_SUBSYSTEM] = PRV_SKIPDUPNULL,
|
||||
[CH_RANK] = PRV_SKIPDUPNULL, /* Switch to task of same rank */
|
||||
[CH_IDLE] = PRV_SKIPDUPNULL,
|
||||
};
|
||||
|
||||
static const struct model_pvt_spec pvt_spec = {
|
||||
@ -189,6 +213,7 @@ static const int th_track[CH_MAX] = {
|
||||
[CH_APPID] = TRACK_TH_RUN,
|
||||
[CH_SUBSYSTEM] = TRACK_TH_ACT,
|
||||
[CH_RANK] = TRACK_TH_RUN,
|
||||
[CH_IDLE] = TRACK_TH_RUN,
|
||||
};
|
||||
|
||||
static const int cpu_track[CH_MAX] = {
|
||||
@ -198,6 +223,7 @@ static const int cpu_track[CH_MAX] = {
|
||||
[CH_APPID] = TRACK_TH_RUN,
|
||||
[CH_SUBSYSTEM] = TRACK_TH_RUN,
|
||||
[CH_RANK] = TRACK_TH_RUN,
|
||||
[CH_IDLE] = TRACK_TH_RUN,
|
||||
};
|
||||
|
||||
/* ----------------- chan_spec ------------------ */
|
||||
@ -279,6 +305,19 @@ model_nosv_create(struct emu *emu)
|
||||
}
|
||||
}
|
||||
|
||||
struct nosv_emu *e = calloc(1, sizeof(struct nosv_emu));
|
||||
if (e == NULL) {
|
||||
err("calloc failed:");
|
||||
return -1;
|
||||
}
|
||||
|
||||
extend_set(&emu->ext, model_id, e);
|
||||
|
||||
if (model_nosv_breakdown_create(emu) != 0) {
|
||||
err("model_nosv_breakdown_create failed");
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -295,6 +334,28 @@ model_nosv_connect(struct emu *emu)
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (emu->args.breakdown && model_nosv_breakdown_connect(emu) != 0) {
|
||||
err("model_nosv_breakdown_connect failed");
|
||||
return -1;
|
||||
}
|
||||
|
||||
for (struct thread *th = emu->system.threads; th; th = th->gnext) {
|
||||
struct nosv_thread *mth = EXT(th, model_id);
|
||||
struct chan *idle = &mth->m.ch[CH_IDLE];
|
||||
/* By default set all threads as Progressing */
|
||||
if (chan_set(idle, value_int64(ST_PROGRESSING)) != 0) {
|
||||
err("chan_push idle failed");
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
for (struct cpu *cpu = emu->system.cpus; cpu; cpu = cpu->next) {
|
||||
struct nosv_cpu *mcpu = EXT(cpu, model_id);
|
||||
struct mux *mux = &mcpu->m.track[CH_IDLE].mux;
|
||||
/* Emit Resting when a CPU has no running threads */
|
||||
mux_set_default(mux, value_int64(ST_RESTING));
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -341,8 +402,12 @@ finish_pvt(struct emu *emu, const char *name)
|
||||
return -1;
|
||||
}
|
||||
struct pcf *pcf = pvt_get_pcf(pvt);
|
||||
long typeid = pvt_type[CH_TYPE];
|
||||
int typeid = pvt_type[CH_TYPE];
|
||||
struct pcf_type *pcftype = pcf_find_type(pcf, typeid);
|
||||
if (pcftype == NULL) {
|
||||
err("cannot find %s pcf type %d", name, typeid);
|
||||
return -1;
|
||||
}
|
||||
|
||||
for (struct proc *p = sys->procs; p; p = p->gnext) {
|
||||
struct nosv_proc *proc = EXT(p, model_id);
|
||||
@ -370,6 +435,11 @@ model_nosv_finish(struct emu *emu)
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (model_nosv_breakdown_finish(emu, pcf_labels) != 0) {
|
||||
err("model_nosv_breakdown_finish failed");
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* When running in linter mode perform additional checks */
|
||||
if (emu->args.linter_mode && end_lint(emu) != 0) {
|
||||
err("end_lint failed");
|
||||
|
@ -8,6 +8,9 @@
|
||||
#include "emu_ev.h"
|
||||
#include "extend.h"
|
||||
#include "model_thread.h"
|
||||
#include "ovni.h"
|
||||
#include "proc.h"
|
||||
#include "task.h"
|
||||
#include "thread.h"
|
||||
#include "value.h"
|
||||
|
||||
@ -95,7 +98,7 @@ static const int fn_table[256][256][3] = {
|
||||
};
|
||||
|
||||
static int
|
||||
process_ev(struct emu *emu)
|
||||
simple(struct emu *emu)
|
||||
{
|
||||
if (!emu->thread->is_running) {
|
||||
err("current thread %d not running", emu->thread->tid);
|
||||
@ -122,6 +125,263 @@ process_ev(struct emu *emu)
|
||||
return -1;
|
||||
}
|
||||
|
||||
static int
|
||||
create_task(struct emu *emu)
|
||||
{
|
||||
if (emu->ev->payload_size != 8) {
|
||||
err("unexpected payload size");
|
||||
return -1;
|
||||
}
|
||||
|
||||
uint32_t taskid = emu->ev->payload->u32[0];
|
||||
uint32_t typeid = emu->ev->payload->u32[1];
|
||||
|
||||
if (taskid == 0) {
|
||||
err("taskid cannot be 0");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (typeid == 0) {
|
||||
err("typeid cannot be 0");
|
||||
return -1;
|
||||
}
|
||||
|
||||
struct openmp_proc *proc = EXT(emu->proc, 'P');
|
||||
struct task_info *info = &proc->task_info;
|
||||
|
||||
/* OpenMP submits inline tasks without pausing the previous
|
||||
* task, so we relax the model to allow this for now. */
|
||||
uint32_t flags = TASK_FLAG_RELAX_NESTING;
|
||||
|
||||
if (task_create(info, typeid, taskid, flags) != 0) {
|
||||
err("task_create failed");
|
||||
return -1;
|
||||
}
|
||||
|
||||
dbg("task created with taskid %u", taskid);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
update_task(struct emu *emu)
|
||||
{
|
||||
if (emu->ev->payload_size < 4) {
|
||||
err("missing task id in payload");
|
||||
return -1;
|
||||
}
|
||||
|
||||
uint32_t taskid = emu->ev->payload->u32[0];
|
||||
|
||||
if (taskid == 0) {
|
||||
err("taskid cannot be 0");
|
||||
return -1;
|
||||
}
|
||||
|
||||
struct openmp_thread *th = EXT(emu->thread, 'P');
|
||||
struct openmp_proc *proc = EXT(emu->proc, 'P');
|
||||
|
||||
struct task_info *info = &proc->task_info;
|
||||
struct task_stack *stack = &th->task_stack;
|
||||
|
||||
struct task *task = task_find(info->tasks, taskid);
|
||||
|
||||
if (task == NULL) {
|
||||
err("cannot find task with id %u", taskid);
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* OpenMP doesn't have parallel tasks */
|
||||
uint32_t body_id = 1;
|
||||
|
||||
if (emu->ev->v == 'x') {
|
||||
if (task_execute(stack, task, body_id) != 0) {
|
||||
err("cannot change task state to running");
|
||||
return -1;
|
||||
}
|
||||
if (chan_push(&th->m.ch[CH_TASKID], value_int64(task->id)) != 0) {
|
||||
err("chan_push taskid failed");
|
||||
return -1;
|
||||
}
|
||||
if (chan_push(&th->m.ch[CH_LABEL], value_int64(task->type->gid)) != 0) {
|
||||
err("chan_push task label failed");
|
||||
return -1;
|
||||
}
|
||||
} else if (emu->ev->v == 'e') {
|
||||
if (task_end(stack, task, body_id) != 0) {
|
||||
err("cannot change task state to end");
|
||||
return -1;
|
||||
}
|
||||
if (chan_pop(&th->m.ch[CH_TASKID], value_int64(task->id)) != 0) {
|
||||
err("chan_pop taskid failed");
|
||||
return -1;
|
||||
}
|
||||
if (chan_pop(&th->m.ch[CH_LABEL], value_int64(task->type->gid)) != 0) {
|
||||
err("chan_pop task label failed");
|
||||
return -1;
|
||||
}
|
||||
} else {
|
||||
err("unexpected task event %c", emu->ev->v);
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
pre_task(struct emu *emu)
|
||||
{
|
||||
int ret = 0;
|
||||
switch (emu->ev->v) {
|
||||
case 'c':
|
||||
ret = create_task(emu);
|
||||
break;
|
||||
case 'x':
|
||||
case 'e':
|
||||
ret = update_task(emu);
|
||||
break;
|
||||
default:
|
||||
err("unexpected task event value");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (ret != 0) {
|
||||
err("cannot update task state");
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
pre_type(struct emu *emu)
|
||||
{
|
||||
uint8_t value = emu->ev->v;
|
||||
|
||||
if (value != 'c') {
|
||||
err("unexpected event value %c", value);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (!emu->ev->is_jumbo) {
|
||||
err("expecting a jumbo event");
|
||||
return -1;
|
||||
}
|
||||
|
||||
const uint8_t *data = &emu->ev->payload->jumbo.data[0];
|
||||
uint32_t typeid;
|
||||
memcpy(&typeid, data, 4); /* May be unaligned */
|
||||
data += 4;
|
||||
|
||||
const char *label = (const char *) data;
|
||||
|
||||
struct openmp_proc *proc = EXT(emu->proc, 'P');
|
||||
struct task_info *info = &proc->task_info;
|
||||
|
||||
/* It will be used for tasks and worksharings. */
|
||||
if (task_type_create(info, typeid, label) != 0) {
|
||||
err("task_type_create failed");
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
update_ws_state(struct emu *emu, uint8_t action)
|
||||
{
|
||||
if (emu->ev->payload_size < 4) {
|
||||
err("missing worksharing id in payload");
|
||||
return -1;
|
||||
}
|
||||
|
||||
uint32_t typeid = emu->ev->payload->u32[0];
|
||||
|
||||
if (typeid == 0) {
|
||||
err("worksharing type id cannot be 0");
|
||||
return -1;
|
||||
}
|
||||
|
||||
struct openmp_thread *th = EXT(emu->thread, 'P');
|
||||
struct openmp_proc *proc = EXT(emu->proc, 'P');
|
||||
|
||||
struct task_info *info = &proc->task_info;
|
||||
|
||||
/* Worksharings share the task type */
|
||||
struct task_type *ttype = task_type_find(info->types, typeid);
|
||||
|
||||
if (ttype == NULL) {
|
||||
err("cannot find ws with type %"PRIu32, typeid);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (action == 'x') {
|
||||
if (chan_push(&th->m.ch[CH_LABEL], value_int64(ttype->gid)) != 0) {
|
||||
err("chan_push worksharing label failed");
|
||||
return -1;
|
||||
}
|
||||
} else {
|
||||
if (chan_pop(&th->m.ch[CH_LABEL], value_int64(ttype->gid)) != 0) {
|
||||
err("chan_pop worksharing label failed");
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
pre_worksharing(struct emu *emu)
|
||||
{
|
||||
int ret = 0;
|
||||
switch (emu->ev->v) {
|
||||
case 'x':
|
||||
case 'e':
|
||||
ret = update_ws_state(emu, emu->ev->v);
|
||||
break;
|
||||
default:
|
||||
err("unexpected ws event value %c", emu->ev->v);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (ret != 0) {
|
||||
err("cannot update worksharing channels");
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
process_ev(struct emu *emu)
|
||||
{
|
||||
if (!emu->thread->is_running) {
|
||||
err("current thread %d not running", emu->thread->tid);
|
||||
return -1;
|
||||
}
|
||||
switch (emu->ev->c) {
|
||||
case 'B':
|
||||
case 'I':
|
||||
case 'W':
|
||||
case 'T':
|
||||
case 'A':
|
||||
case 'M':
|
||||
case 'H':
|
||||
case 'C':
|
||||
return simple(emu);
|
||||
case 'P':
|
||||
return pre_task(emu);
|
||||
case 'O':
|
||||
return pre_type(emu);
|
||||
case 'Q':
|
||||
return pre_worksharing(emu);
|
||||
}
|
||||
|
||||
err("unknown event category");
|
||||
return -1;
|
||||
}
|
||||
|
||||
int
|
||||
model_openmp_event(struct emu *emu)
|
||||
{
|
||||
|
@ -5,6 +5,7 @@
|
||||
#define OPENMP_PRIV_H
|
||||
|
||||
#include "emu.h"
|
||||
#include "task.h"
|
||||
#include "model_cpu.h"
|
||||
#include "model_thread.h"
|
||||
|
||||
@ -12,6 +13,8 @@
|
||||
|
||||
enum openmp_chan {
|
||||
CH_SUBSYSTEM = 0,
|
||||
CH_LABEL,
|
||||
CH_TASKID,
|
||||
CH_MAX,
|
||||
};
|
||||
|
||||
@ -55,12 +58,18 @@ enum openmp_function_values {
|
||||
|
||||
struct openmp_thread {
|
||||
struct model_thread m;
|
||||
struct task_stack task_stack;
|
||||
};
|
||||
|
||||
struct openmp_cpu {
|
||||
struct model_cpu m;
|
||||
};
|
||||
|
||||
struct openmp_proc {
|
||||
/* Shared among tasks and ws */
|
||||
struct task_info task_info;
|
||||
};
|
||||
|
||||
int model_openmp_probe(struct emu *emu);
|
||||
int model_openmp_create(struct emu *emu);
|
||||
int model_openmp_connect(struct emu *emu);
|
||||
|
@ -15,8 +15,10 @@
|
||||
#include "model_cpu.h"
|
||||
#include "model_pvt.h"
|
||||
#include "model_thread.h"
|
||||
#include "proc.h"
|
||||
#include "pv/pcf.h"
|
||||
#include "pv/prv.h"
|
||||
#include "pv/pvt.h"
|
||||
#include "system.h"
|
||||
#include "thread.h"
|
||||
#include "track.h"
|
||||
@ -65,12 +67,22 @@ static struct ev_decl model_evlist[] = {
|
||||
PAIR_B("PCf", "PCF", "fork call")
|
||||
PAIR_B("PCi", "PCI", "initialization")
|
||||
|
||||
/* Task or worksharing type */
|
||||
{ "POc+(u32 typeid, str label)", "creates a type %{typeid} with label \"%{label}\"" },
|
||||
|
||||
{ "PPc(u32 taskid, u32 typeid)", "creates the task %{taskid} with type %{typeid}" },
|
||||
{ "PPx(u32 taskid)", "executes the task %{taskid}" },
|
||||
{ "PPe(u32 taskid)", "ends the task %{taskid}" },
|
||||
|
||||
{ "PQx(u32 typeid)", "begins worksharing with type %{typeid}" },
|
||||
{ "PQe(u32 typeid)", "ends worksharing with type %{typeid}" },
|
||||
|
||||
{ NULL, NULL },
|
||||
};
|
||||
|
||||
struct model_spec model_openmp = {
|
||||
.name = model_name,
|
||||
.version = "1.1.0",
|
||||
.version = "1.2.0",
|
||||
.evlist = model_evlist,
|
||||
.model = model_id,
|
||||
.create = model_openmp_create,
|
||||
@ -84,24 +96,34 @@ struct model_spec model_openmp = {
|
||||
|
||||
static const char *chan_name[CH_MAX] = {
|
||||
[CH_SUBSYSTEM] = "subsystem",
|
||||
[CH_LABEL] = "label",
|
||||
[CH_TASKID] = "task ID",
|
||||
};
|
||||
|
||||
static const int chan_stack[CH_MAX] = {
|
||||
[CH_SUBSYSTEM] = 1,
|
||||
[CH_LABEL] = 1,
|
||||
[CH_TASKID] = 1,
|
||||
};
|
||||
|
||||
static const int chan_dup[CH_MAX] = {
|
||||
[CH_SUBSYSTEM] = 1,
|
||||
[CH_LABEL] = 1, /* Two tasks nested with same type */
|
||||
[CH_TASKID] = 1,
|
||||
};
|
||||
|
||||
/* ----------------- pvt ------------------ */
|
||||
|
||||
static const int pvt_type[CH_MAX] = {
|
||||
[CH_SUBSYSTEM] = PRV_OPENMP_SUBSYSTEM,
|
||||
[CH_LABEL] = PRV_OPENMP_LABEL,
|
||||
[CH_TASKID] = PRV_OPENMP_TASKID,
|
||||
};
|
||||
|
||||
static const char *pcf_prefix[CH_MAX] = {
|
||||
[CH_SUBSYSTEM] = "OpenMP subsystem",
|
||||
[CH_LABEL] = "OpenMP label",
|
||||
[CH_TASKID] = "OpenMP task ID",
|
||||
};
|
||||
|
||||
static const struct pcf_value_label openmp_subsystem_values[] = {
|
||||
@ -149,7 +171,9 @@ static const struct pcf_value_label *pcf_labels[CH_MAX] = {
|
||||
};
|
||||
|
||||
static const long prv_flags[CH_MAX] = {
|
||||
[CH_SUBSYSTEM] = PRV_EMITDUP,
|
||||
[CH_SUBSYSTEM] = PRV_SKIPDUPNULL,
|
||||
[CH_LABEL] = PRV_SKIPDUPNULL,
|
||||
[CH_TASKID] = PRV_SKIPDUPNULL,
|
||||
};
|
||||
|
||||
static const struct model_pvt_spec pvt_spec = {
|
||||
@ -163,10 +187,14 @@ static const struct model_pvt_spec pvt_spec = {
|
||||
|
||||
static const int th_track[CH_MAX] = {
|
||||
[CH_SUBSYSTEM] = TRACK_TH_ACT,
|
||||
[CH_LABEL] = TRACK_TH_ACT,
|
||||
[CH_TASKID] = TRACK_TH_ACT,
|
||||
};
|
||||
|
||||
static const int cpu_track[CH_MAX] = {
|
||||
[CH_SUBSYSTEM] = TRACK_TH_RUN,
|
||||
[CH_LABEL] = TRACK_TH_RUN,
|
||||
[CH_TASKID] = TRACK_TH_RUN,
|
||||
};
|
||||
|
||||
/* ----------------- chan_spec ------------------ */
|
||||
@ -213,9 +241,24 @@ model_openmp_probe(struct emu *emu)
|
||||
return model_version_probe(&model_openmp, emu);
|
||||
}
|
||||
|
||||
static int
|
||||
init_proc(struct proc *sysproc)
|
||||
{
|
||||
struct openmp_proc *proc = calloc(1, sizeof(struct openmp_proc));
|
||||
if (proc == NULL) {
|
||||
err("calloc failed:");
|
||||
return -1;
|
||||
}
|
||||
|
||||
extend_set(&sysproc->ext, model_id, proc);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
model_openmp_create(struct emu *emu)
|
||||
{
|
||||
|
||||
if (model_thread_create(emu, &th_spec) != 0) {
|
||||
err("model_thread_init failed");
|
||||
return -1;
|
||||
@ -226,6 +269,15 @@ model_openmp_create(struct emu *emu)
|
||||
return -1;
|
||||
}
|
||||
|
||||
struct system *sys = &emu->system;
|
||||
|
||||
for (struct proc *p = sys->procs; p; p = p->gnext) {
|
||||
if (init_proc(p) != 0) {
|
||||
err("init_proc failed");
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -245,6 +297,44 @@ model_openmp_connect(struct emu *emu)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
create_pcf_type(struct system *sys, struct pcf *pcf, long typeid)
|
||||
{
|
||||
struct pcf_type *pcftype = pcf_find_type(pcf, (int) typeid);
|
||||
|
||||
for (struct proc *p = sys->procs; p; p = p->gnext) {
|
||||
struct openmp_proc *proc = EXT(p, model_id);
|
||||
struct task_info *info = &proc->task_info;
|
||||
if (task_create_pcf_types(pcftype, info->types) != 0) {
|
||||
err("task_create_pcf_types failed");
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
finish_pvt(struct emu *emu, const char *name)
|
||||
{
|
||||
struct system *sys = &emu->system;
|
||||
|
||||
/* Emit task types for all channel types and processes */
|
||||
struct pvt *pvt = recorder_find_pvt(&emu->recorder, name);
|
||||
if (pvt == NULL) {
|
||||
err("cannot find pvt with name '%s'", name);
|
||||
return -1;
|
||||
}
|
||||
|
||||
struct pcf *pcf = pvt_get_pcf(pvt);
|
||||
if (create_pcf_type(sys, pcf, pvt_type[CH_LABEL]) != 0) {
|
||||
err("create_pcf_type failed");
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
end_lint(struct emu *emu)
|
||||
{
|
||||
@ -278,6 +368,17 @@ end_lint(struct emu *emu)
|
||||
int
|
||||
model_openmp_finish(struct emu *emu)
|
||||
{
|
||||
/* Fill task types */
|
||||
if (finish_pvt(emu, "thread") != 0) {
|
||||
err("finish_pvt thread failed");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (finish_pvt(emu, "cpu") != 0) {
|
||||
err("finish_pvt cpu failed");
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* When running in linter mode perform additional checks */
|
||||
if (emu->args.linter_mode && end_lint(emu) != 0) {
|
||||
err("end_lint failed");
|
||||
|
@ -16,6 +16,7 @@
|
||||
#include "proc.h"
|
||||
#include "thread.h"
|
||||
#include "value.h"
|
||||
#include "mark.h"
|
||||
|
||||
static int
|
||||
pre_thread_execute(struct emu *emu, struct thread *th)
|
||||
@ -228,7 +229,7 @@ pre_affinity_set(struct emu *emu)
|
||||
}
|
||||
|
||||
if (emu->ev->payload_size != 4) {
|
||||
err("unexpected payload size %d", emu->ev->payload_size);
|
||||
err("unexpected payload size %zd", emu->ev->payload_size);
|
||||
return -1;
|
||||
}
|
||||
|
||||
@ -264,7 +265,7 @@ static int
|
||||
pre_affinity_remote(struct emu *emu)
|
||||
{
|
||||
if (emu->ev->payload_size != 8) {
|
||||
err("unexpected payload size %d", emu->ev->payload_size);
|
||||
err("unexpected payload size %zd", emu->ev->payload_size);
|
||||
return -1;
|
||||
}
|
||||
|
||||
@ -389,18 +390,18 @@ pre_burst(struct emu *emu)
|
||||
for (int i = 0; i < n; i++)
|
||||
deltas[i] = th->burst_time[i + 1] - th->burst_time[i];
|
||||
|
||||
qsort(deltas, n, sizeof(int64_t), compare_int64);
|
||||
qsort(deltas, (size_t) n, sizeof(int64_t), compare_int64);
|
||||
|
||||
double avg = 0.0;
|
||||
double maxdelta = 0;
|
||||
for (int i = 0; i < n; i++) {
|
||||
if (deltas[i] > maxdelta)
|
||||
maxdelta = deltas[i];
|
||||
avg += deltas[i];
|
||||
if ((double) deltas[i] > maxdelta)
|
||||
maxdelta = (double) deltas[i];
|
||||
avg += (double) deltas[i];
|
||||
}
|
||||
|
||||
avg /= (double) n;
|
||||
double median = deltas[n / 2];
|
||||
double median = (double) deltas[n / 2];
|
||||
|
||||
info("%s burst stats: median/avg/max = %3.0f/%3.0f/%3.0f ns",
|
||||
emu->loom->id, median, avg, maxdelta);
|
||||
@ -436,7 +437,7 @@ pre_flush(struct emu *emu)
|
||||
double flush_ms = (double) flush_ns * 1e-6;
|
||||
/* Avoid last flush warnings */
|
||||
if (flush_ms > 10.0 && emu->thread->is_running)
|
||||
warn("large flush of %.1f ms at dclock=%ld ns in tid=%d",
|
||||
warn("large flush of %.1f ms at dclock=%"PRIi64" ns in tid=%d",
|
||||
flush_ms,
|
||||
emu->ev->dclock,
|
||||
emu->thread->tid);
|
||||
@ -477,6 +478,8 @@ model_ovni_event(struct emu *emu)
|
||||
case 'U':
|
||||
/* Ignore sorting events */
|
||||
return 0;
|
||||
case 'M':
|
||||
return mark_event(emu);
|
||||
default:
|
||||
err("unknown ovni event category %c",
|
||||
emu->ev->c);
|
||||
|
668
src/emu/ovni/mark.c
Normal file
668
src/emu/ovni/mark.c
Normal file
@ -0,0 +1,668 @@
|
||||
#include "mark.h"
|
||||
|
||||
#include "chan.h"
|
||||
#include "cpu.h"
|
||||
#include "emu.h"
|
||||
#include "emu_ev.h"
|
||||
#include "emu_prv.h"
|
||||
#include "inttypes.h"
|
||||
#include "ovni.h"
|
||||
#include "ovni_priv.h"
|
||||
#include "parson.h"
|
||||
#include "pv/pcf.h"
|
||||
#include "pv/prv.h"
|
||||
#include "pv/pvt.h"
|
||||
#include "thread.h"
|
||||
#include "track.h"
|
||||
#include "uthash.h"
|
||||
#include <errno.h>
|
||||
|
||||
struct mark_label {
|
||||
int64_t value;
|
||||
char label[MAX_PCF_LABEL];
|
||||
UT_hash_handle hh; /* Indexed by value */
|
||||
};
|
||||
|
||||
struct mark_type {
|
||||
long type;
|
||||
long prvtype;
|
||||
long index; /* From 0 to ntypes - 1 */
|
||||
enum chan_type ctype;
|
||||
struct mark_label *labels; /* Hash table of labels */
|
||||
char title[MAX_PCF_LABEL];
|
||||
UT_hash_handle hh; /* Indexed by type */
|
||||
};
|
||||
|
||||
static int
|
||||
parse_number(const char *str, int64_t *result)
|
||||
{
|
||||
errno = 0;
|
||||
char *endptr = NULL;
|
||||
int64_t n = strtoll(str, &endptr, 10);
|
||||
|
||||
if (errno != 0 || endptr == str || endptr[0] != '\0') {
|
||||
err("failed to parse number: %s", str);
|
||||
return -1;
|
||||
}
|
||||
|
||||
*result = n;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct mark_label *
|
||||
find_label(struct mark_type *t, int64_t value)
|
||||
{
|
||||
struct mark_label *l;
|
||||
HASH_FIND(hh, t->labels, &value, sizeof(value), l);
|
||||
return l;
|
||||
}
|
||||
|
||||
static int
|
||||
add_label(struct mark_type *t, int64_t value, const char *label)
|
||||
{
|
||||
struct mark_label *l = find_label(t, value);
|
||||
|
||||
if (l != NULL) {
|
||||
if (strcmp(l->label, label) == 0) {
|
||||
/* Already exists with the same label, all good */
|
||||
return 0;
|
||||
} else {
|
||||
err("mark value %" PRIi64 " already defined with label %s", value, l->label);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
l = calloc(1, sizeof(*l));
|
||||
if (l == NULL) {
|
||||
err("calloc failed:");
|
||||
return -1;
|
||||
}
|
||||
|
||||
l->value = value;
|
||||
|
||||
int len = snprintf(l->label, MAX_PCF_LABEL, "%s", label);
|
||||
if (len >= MAX_PCF_LABEL) {
|
||||
err("mark label too long: %s", label);
|
||||
return -1;
|
||||
}
|
||||
|
||||
HASH_ADD(hh, t->labels, value, sizeof(value), l);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
parse_labels(struct mark_type *t, JSON_Object *labels)
|
||||
{
|
||||
/* It may happen that we call this function several times with
|
||||
* overlapping subsets of values. The only restriction is that we don't
|
||||
* define two values with different label. */
|
||||
|
||||
size_t n = json_object_get_count(labels);
|
||||
for (size_t i = 0; i < n; i++) {
|
||||
const char *valuestr = json_object_get_name(labels, i);
|
||||
if (valuestr == NULL) {
|
||||
err("json_object_get_name failed");
|
||||
return -1;
|
||||
}
|
||||
|
||||
int64_t value;
|
||||
if (parse_number(valuestr, &value) != 0) {
|
||||
err("parse_number failed");
|
||||
return -1;
|
||||
}
|
||||
|
||||
JSON_Value *labelval = json_object_get_value_at(labels, i);
|
||||
if (labelval == NULL) {
|
||||
err("json_object_get_value_at failed");
|
||||
return -1;
|
||||
}
|
||||
|
||||
const char *label = json_value_get_string(labelval);
|
||||
if (label == NULL) {
|
||||
err("json_value_get_string() for label failed");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (add_label(t, value, label) != 0) {
|
||||
err("add_label() failed");
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct mark_type *
|
||||
find_mark_type(struct ovni_mark_emu *m, long type)
|
||||
{
|
||||
struct mark_type *t;
|
||||
HASH_FIND_LONG(m->types, &type, t);
|
||||
return t;
|
||||
}
|
||||
|
||||
static struct mark_type *
|
||||
create_mark_type(struct ovni_mark_emu *m, long type, enum chan_type ctype, const char *title)
|
||||
{
|
||||
struct mark_type *t = find_mark_type(m, type);
|
||||
|
||||
if (t != NULL) {
|
||||
err("mark type %ld already defined", type);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
t = calloc(1, sizeof(*t));
|
||||
if (t == NULL) {
|
||||
err("calloc failed:");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
t->type = type;
|
||||
t->ctype = ctype;
|
||||
t->prvtype = type + PRV_OVNI_MARK;
|
||||
t->index = m->ntypes;
|
||||
|
||||
int len = snprintf(t->title, MAX_PCF_LABEL, "%s", title);
|
||||
if (len >= MAX_PCF_LABEL) {
|
||||
err("mark title too long: %s", title);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
HASH_ADD_LONG(m->types, type, t);
|
||||
m->ntypes++;
|
||||
|
||||
return t;
|
||||
}
|
||||
|
||||
static int
|
||||
parse_mark(struct ovni_mark_emu *m, const char *typestr, JSON_Value *markval)
|
||||
{
|
||||
errno = 0;
|
||||
char *endptr = NULL;
|
||||
long type = strtol(typestr, &endptr, 10);
|
||||
|
||||
if (errno != 0 || endptr == typestr || endptr[0] != '\0') {
|
||||
err("failed to parse type number: %s", typestr);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (type < 0 || type >= 100) {
|
||||
err("mark type should be in [0, 100) range: %ld", type);
|
||||
return -1;
|
||||
}
|
||||
|
||||
JSON_Object *mark = json_value_get_object(markval);
|
||||
if (mark == NULL) {
|
||||
err("json_value_get_object() failed");
|
||||
return -1;
|
||||
}
|
||||
|
||||
const char *title = json_object_get_string(mark, "title");
|
||||
if (title == NULL) {
|
||||
err("json_object_get_string() for title failed");
|
||||
return -1;
|
||||
}
|
||||
|
||||
const char *chan_type = json_object_get_string(mark, "chan_type");
|
||||
if (chan_type == NULL) {
|
||||
err("json_object_get_string() for chan_type failed");
|
||||
return -1;
|
||||
}
|
||||
|
||||
enum chan_type ctype;
|
||||
if (strcmp(chan_type, "single") == 0) {
|
||||
ctype = CHAN_SINGLE;
|
||||
} else if (strcmp(chan_type, "stack") == 0) {
|
||||
ctype = CHAN_STACK;
|
||||
} else {
|
||||
err("chan_type %s not understood", chan_type);
|
||||
return -1;
|
||||
}
|
||||
|
||||
struct mark_type *t = find_mark_type(m, type);
|
||||
if (t == NULL) {
|
||||
t = create_mark_type(m, type, ctype, title);
|
||||
if (t == NULL) {
|
||||
err("cannot create mark type");
|
||||
return -1;
|
||||
}
|
||||
} else {
|
||||
/* It may already exist as defined by other threads, so ensure
|
||||
* they have the same title. */
|
||||
if (strcmp(t->title, title) != 0) {
|
||||
err("mark with type %ld already registered with another title", type);
|
||||
err(" old: %s", t->title);
|
||||
err(" new: %s", title);
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* And also the same channel type */
|
||||
if (t->ctype != ctype) {
|
||||
err("mark with type %ld already registered with another channel type", type);
|
||||
err(" old: %s", t->ctype == CHAN_SINGLE ? "single" : "stack");
|
||||
err(" new: %s", ctype == CHAN_SINGLE ? "single" : "stack");
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
/* The labels are optional */
|
||||
if (json_object_has_value(mark, "labels")) {
|
||||
|
||||
JSON_Object *labels = json_object_get_object(mark, "labels");
|
||||
if (labels == NULL) {
|
||||
err("json_object_get_object() for labels failed");
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Now populate the mark type with all value labels */
|
||||
|
||||
if (parse_labels(t, labels) != 0) {
|
||||
err("cannot parse labels");
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
scan_thread(struct ovni_mark_emu *memu, struct thread *t)
|
||||
{
|
||||
JSON_Object *obj = json_object_dotget_object(t->meta, "ovni.mark");
|
||||
|
||||
/* No marks in this thread */
|
||||
if (obj == NULL)
|
||||
return 0;
|
||||
|
||||
size_t n = json_object_get_count(obj);
|
||||
for (size_t i = 0; i < n; i++) {
|
||||
const char *typestr = json_object_get_name(obj, i);
|
||||
if (typestr == NULL) {
|
||||
err("json_object_get_name failed");
|
||||
return -1;
|
||||
}
|
||||
JSON_Value *markval = json_object_get_value_at(obj, i);
|
||||
if (markval == NULL) {
|
||||
err("json_object_get_value_at failed");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (parse_mark(memu, typestr, markval) != 0) {
|
||||
err("cannot parse mark");
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
create_thread_chan(struct ovni_mark_emu *m, struct bay *bay, struct thread *th)
|
||||
{
|
||||
struct ovni_thread *oth = EXT(th, 'O');
|
||||
struct ovni_mark_thread *t = &oth->mark;
|
||||
|
||||
/* Create as many channels as required */
|
||||
t->channels = calloc((size_t) m->ntypes, sizeof(struct chan));
|
||||
if (t->channels == NULL) {
|
||||
err("calloc failed:");
|
||||
return -1;
|
||||
}
|
||||
|
||||
t->nchannels = m->ntypes;
|
||||
|
||||
struct mark_type *type;
|
||||
for (type = m->types; type; type = type->hh.next) {
|
||||
/* TODO: We may use a vector of thread channels in every type to
|
||||
* avoid the double hash access in events */
|
||||
long i = type->index;
|
||||
struct chan *ch = &t->channels[i];
|
||||
chan_init(ch, type->ctype, "thread%"PRIi64".mark%ld",
|
||||
th->gindex, type->type);
|
||||
|
||||
/* Allow duplicates */
|
||||
chan_prop_set(ch, CHAN_ALLOW_DUP, 1);
|
||||
|
||||
if (bay_register(bay, ch) != 0) {
|
||||
err("bay_register failed");
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
/* Setup tracking */
|
||||
t->track = calloc((size_t) m->ntypes, sizeof(struct track));
|
||||
if (t->track == NULL) {
|
||||
err("calloc failed:");
|
||||
return -1;
|
||||
}
|
||||
|
||||
for (type = m->types; type; type = type->hh.next) {
|
||||
long i = type->index;
|
||||
struct track *track = &t->track[i];
|
||||
/* For now only tracking to active thread is supported */
|
||||
if (track_init(track, bay, TRACK_TYPE_TH, TRACK_TH_ACT,
|
||||
"thread%"PRIi64".mark%ld",
|
||||
th->gindex, type->type) != 0) {
|
||||
err("track_init failed");
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
init_cpu(struct ovni_mark_emu *m, struct bay *bay, struct cpu *cpu)
|
||||
{
|
||||
struct ovni_cpu *ocpu = EXT(cpu, 'O');
|
||||
struct ovni_mark_cpu *c = &ocpu->mark;
|
||||
|
||||
/* Setup tracking */
|
||||
c->track = calloc((size_t) m->ntypes, sizeof(struct track));
|
||||
if (c->track == NULL) {
|
||||
err("calloc failed:");
|
||||
return -1;
|
||||
}
|
||||
|
||||
struct mark_type *type;
|
||||
for (type = m->types; type; type = type->hh.next) {
|
||||
long i = type->index;
|
||||
struct track *track = &c->track[i];
|
||||
/* For now only tracking to running thread is supported */
|
||||
if (track_init(track, bay, TRACK_TYPE_TH, TRACK_TH_RUN,
|
||||
"cpu%"PRIi64".mark%ld",
|
||||
cpu->gindex, type->type) != 0) {
|
||||
err("track_init failed");
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Scans streams for marks and creates the mark channels */
|
||||
int
|
||||
mark_create(struct emu *emu)
|
||||
{
|
||||
struct ovni_emu *oemu = EXT(emu, 'O');
|
||||
struct ovni_mark_emu *memu = &oemu->mark;
|
||||
|
||||
memset(memu, 0, sizeof(*memu));
|
||||
|
||||
for (struct thread *th = emu->system.threads; th; th = th->gnext) {
|
||||
if (scan_thread(memu, th) != 0) {
|
||||
err("scan_thread failed");
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
if (memu->ntypes == 0)
|
||||
return 0;
|
||||
|
||||
for (struct thread *th = emu->system.threads; th; th = th->gnext) {
|
||||
if (create_thread_chan(memu, &emu->bay, th) != 0) {
|
||||
err("create_thread_chan failed");
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
for (struct cpu *cpu = emu->system.cpus; cpu; cpu = cpu->next) {
|
||||
if (init_cpu(memu, &emu->bay, cpu) != 0) {
|
||||
err("init_cpu failed");
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
connect_thread_prv(struct emu *emu, struct thread *sth, struct prv *prv)
|
||||
{
|
||||
struct ovni_emu *oemu = EXT(emu, 'O');
|
||||
struct ovni_mark_emu *memu = &oemu->mark;
|
||||
struct ovni_thread *oth = EXT(sth, 'O');
|
||||
struct ovni_mark_thread *mth = &oth->mark;
|
||||
|
||||
|
||||
for (struct mark_type *type = memu->types; type; type = type->hh.next) {
|
||||
/* TODO: We may use a vector of thread channels in every type to
|
||||
* avoid the double hash access in events */
|
||||
long i = type->index;
|
||||
struct chan *ch = &mth->channels[i];
|
||||
struct track *track = &mth->track[i];
|
||||
struct chan *sel = &sth->chan[TH_CHAN_STATE];
|
||||
|
||||
/* Connect the input and sel channel to the mux */
|
||||
if (track_connect_thread(track, ch, sel, 1) != 0) {
|
||||
err("track_connect_thread failed");
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Then connect the output of the tracking module to the prv
|
||||
* trace for the current thread */
|
||||
struct chan *out = track_get_output(track);
|
||||
long row = (long) sth->gindex;
|
||||
long flags = PRV_SKIPDUPNULL;
|
||||
long prvtype = type->prvtype;
|
||||
if (prv_register(prv, row, prvtype, &emu->bay, out, flags)) {
|
||||
err("prv_register failed");
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
create_type(struct pcf *pcf, struct mark_type *type)
|
||||
{
|
||||
struct pcf_type *pcftype = pcf_add_type(pcf, (int) type->prvtype, type->title);
|
||||
if (pcftype == NULL) {
|
||||
err("pcf_add_type failed");
|
||||
return -1;
|
||||
}
|
||||
|
||||
for (struct mark_label *l = type->labels; l; l = l->hh.next) {
|
||||
if (pcf_add_value(pcftype, (int) l->value, l->label) == NULL) {
|
||||
err("pcf_add_value failed");
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
init_pcf(struct emu *emu, struct pcf *pcf)
|
||||
{
|
||||
struct ovni_emu *oemu = EXT(emu, 'O');
|
||||
struct ovni_mark_emu *m = &oemu->mark;
|
||||
|
||||
for (struct mark_type *type = m->types; type; type = type->hh.next) {
|
||||
if (create_type(pcf, type) != 0) {
|
||||
err("create_type failed");
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
connect_thread(struct emu *emu)
|
||||
{
|
||||
/* Get cpu PRV */
|
||||
struct pvt *pvt = recorder_find_pvt(&emu->recorder, "thread");
|
||||
if (pvt == NULL) {
|
||||
err("cannot find thread pvt");
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Connect thread channels to PRV */
|
||||
struct prv *prv = pvt_get_prv(pvt);
|
||||
for (struct thread *t = emu->system.threads; t; t = t->gnext) {
|
||||
if (connect_thread_prv(emu, t, prv) != 0) {
|
||||
err("connect_thread_prv failed");
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
/* Init thread PCF */
|
||||
struct pcf *pcf = pvt_get_pcf(pvt);
|
||||
if (init_pcf(emu, pcf) != 0) {
|
||||
err("init_pcf failed");
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
connect_cpu_prv(struct emu *emu, struct cpu *scpu, struct prv *prv)
|
||||
{
|
||||
struct ovni_emu *oemu = EXT(emu, 'O');
|
||||
struct ovni_mark_emu *memu = &oemu->mark;
|
||||
struct ovni_cpu *ocpu = EXT(scpu, 'O');
|
||||
struct ovni_mark_cpu *mcpu = &ocpu->mark;
|
||||
|
||||
|
||||
for (struct mark_type *type = memu->types; type; type = type->hh.next) {
|
||||
/* NOTE: We may use a vector of thread channels in every type to
|
||||
* avoid the double hash access in events */
|
||||
long i = type->index;
|
||||
struct track *track = &mcpu->track[i];
|
||||
struct chan *sel = cpu_get_th_chan(scpu);
|
||||
|
||||
int64_t nthreads = (int64_t) emu->system.nthreads;
|
||||
if (track_set_select(track, sel, NULL, nthreads) != 0) {
|
||||
err("track_select failed");
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Add each thread as input */
|
||||
for (struct thread *t = emu->system.threads; t; t = t->gnext) {
|
||||
struct ovni_thread *oth = EXT(t, 'O');
|
||||
struct ovni_mark_thread *mth = &oth->mark;
|
||||
|
||||
/* Use the input thread directly */
|
||||
struct chan *inp = &mth->channels[i];
|
||||
|
||||
if (track_set_input(track, t->gindex, inp) != 0) {
|
||||
err("track_add_input failed");
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
/* Then connect the output of the tracking module to the prv
|
||||
* trace for the current thread */
|
||||
struct chan *out = track_get_output(track);
|
||||
long row = (long) scpu->gindex;
|
||||
long flags = PRV_SKIPDUPNULL;
|
||||
long prvtype = type->prvtype;
|
||||
if (prv_register(prv, row, prvtype, &emu->bay, out, flags)) {
|
||||
err("prv_register failed");
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static int
|
||||
connect_cpu(struct emu *emu)
|
||||
{
|
||||
/* Get cpu PRV */
|
||||
struct pvt *pvt = recorder_find_pvt(&emu->recorder, "cpu");
|
||||
if (pvt == NULL) {
|
||||
err("cannot find thread pvt");
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Connect cpu channels to PRV */
|
||||
struct prv *prv = pvt_get_prv(pvt);
|
||||
for (struct cpu *cpu = emu->system.cpus; cpu; cpu = cpu->next) {
|
||||
if (connect_cpu_prv(emu, cpu, prv) != 0) {
|
||||
err("connect_cpu_prv failed");
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
/* Init thread PCF */
|
||||
struct pcf *pcf = pvt_get_pcf(pvt);
|
||||
if (init_pcf(emu, pcf) != 0) {
|
||||
err("init_pcf failed");
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Connect the channels to the output PVTs */
|
||||
int
|
||||
mark_connect(struct emu *emu)
|
||||
{
|
||||
struct ovni_emu *oemu = EXT(emu, 'O');
|
||||
struct ovni_mark_emu *memu = &oemu->mark;
|
||||
|
||||
if (memu->ntypes == 0)
|
||||
return 0;
|
||||
|
||||
if (connect_thread(emu) != 0) {
|
||||
err("connect_thread() failed");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (connect_cpu(emu) != 0) {
|
||||
err("connect_cpu() failed");
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
mark_event(struct emu *emu)
|
||||
{
|
||||
if (emu->ev->payload_size != 8 + 4) {
|
||||
err("unexpected payload size %zd", emu->ev->payload_size);
|
||||
return -1;
|
||||
}
|
||||
|
||||
struct ovni_emu *oemu = EXT(emu, 'O');
|
||||
struct ovni_mark_emu *memu = &oemu->mark;
|
||||
|
||||
int64_t value = emu->ev->payload->i64[0];
|
||||
long type = (long) emu->ev->payload->i32[2]; /* always fits */
|
||||
|
||||
struct mark_type *mc = find_mark_type(memu, type);
|
||||
|
||||
if (mc == NULL) {
|
||||
err("cannot find mark with type %ld", type);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (value == 0) {
|
||||
err("mark value cannot be zero, type %ld", type);
|
||||
return -1;
|
||||
}
|
||||
|
||||
long index = mc->index;
|
||||
struct ovni_thread *oth = EXT(emu->thread, 'O');
|
||||
struct ovni_mark_thread *mth = &oth->mark;
|
||||
|
||||
struct chan *ch = &mth->channels[index];
|
||||
|
||||
switch (emu->ev->v) {
|
||||
case '[':
|
||||
return chan_push(ch, value_int64(value));
|
||||
case ']':
|
||||
return chan_pop(ch, value_int64(value));
|
||||
case '=':
|
||||
return chan_set(ch, value_int64(value));
|
||||
default:
|
||||
err("unknown mark event value %c", emu->ev->v);
|
||||
return -1;
|
||||
}
|
||||
}
|
30
src/emu/ovni/mark.h
Normal file
30
src/emu/ovni/mark.h
Normal file
@ -0,0 +1,30 @@
|
||||
#ifndef MARK_H
|
||||
#define MARK_H
|
||||
|
||||
#include "common.h"
|
||||
#include "chan.h"
|
||||
|
||||
struct emu;
|
||||
struct mark_chan;
|
||||
|
||||
struct ovni_mark_emu {
|
||||
/* Hash table of types of marks */
|
||||
struct mark_type *types;
|
||||
long ntypes;
|
||||
};
|
||||
|
||||
struct ovni_mark_thread {
|
||||
struct track *track;
|
||||
struct chan *channels;
|
||||
long nchannels;
|
||||
};
|
||||
|
||||
struct ovni_mark_cpu {
|
||||
struct track *track;
|
||||
};
|
||||
|
||||
USE_RET int mark_create(struct emu *emu);
|
||||
USE_RET int mark_connect(struct emu *emu);
|
||||
USE_RET int mark_event(struct emu *emu);
|
||||
|
||||
#endif /* MARK_H */
|
@ -1,4 +1,4 @@
|
||||
/* Copyright (c) 2021-2023 Barcelona Supercomputing Center (BSC)
|
||||
/* Copyright (c) 2021-2024 Barcelona Supercomputing Center (BSC)
|
||||
* SPDX-License-Identifier: GPL-3.0-or-later */
|
||||
|
||||
#ifndef OVNI_PRIV_H
|
||||
@ -10,6 +10,7 @@
|
||||
* execution by the kernel. */
|
||||
|
||||
#include "emu.h"
|
||||
#include "mark.h"
|
||||
#include "model_cpu.h"
|
||||
#include "model_thread.h"
|
||||
#include <stdint.h>
|
||||
@ -33,10 +34,17 @@ struct ovni_thread {
|
||||
int64_t burst_time[MAX_BURSTS];
|
||||
|
||||
int64_t flush_start;
|
||||
|
||||
struct ovni_mark_thread mark;
|
||||
};
|
||||
|
||||
struct ovni_cpu {
|
||||
struct model_cpu m;
|
||||
struct ovni_mark_cpu mark;
|
||||
};
|
||||
|
||||
struct ovni_emu {
|
||||
struct ovni_mark_emu mark;
|
||||
};
|
||||
|
||||
int model_ovni_probe(struct emu *emu);
|
||||
|
@ -7,11 +7,13 @@
|
||||
#include "emu.h"
|
||||
#include "emu_prv.h"
|
||||
#include "ev_spec.h"
|
||||
#include "mark.h"
|
||||
#include "model.h"
|
||||
#include "model_chan.h"
|
||||
#include "model_cpu.h"
|
||||
#include "model_pvt.h"
|
||||
#include "model_thread.h"
|
||||
#include "ovni.h"
|
||||
#include "pv/pcf.h"
|
||||
#include "pv/prv.h"
|
||||
#include "system.h"
|
||||
@ -37,12 +39,16 @@ static struct ev_decl model_evlist[] = {
|
||||
PAIR_B("OF[", "OF]", "flushing events to disk")
|
||||
PAIR_E("OU[", "OU]", "unordered event region")
|
||||
|
||||
{ "OM[(i64 value, i32 type)", "push mark with value %{value} from type %{type}" },
|
||||
{ "OM](i64 value, i32 type)", "pop mark with value %{value} from type %{type}" },
|
||||
{ "OM=(i64 value, i32 type)", "set mark with value %{value} from type %{type}" },
|
||||
|
||||
{ NULL, NULL },
|
||||
};
|
||||
|
||||
struct model_spec model_ovni = {
|
||||
.name = model_name,
|
||||
.version = "1.0.0",
|
||||
.version = OVNI_MODEL_VERSION,
|
||||
.evlist = model_evlist,
|
||||
.model = model_id,
|
||||
.create = model_ovni_create,
|
||||
@ -175,6 +181,19 @@ model_ovni_create(struct emu *emu)
|
||||
return -1;
|
||||
}
|
||||
|
||||
struct ovni_emu *oemu = calloc(1, sizeof(*oemu));
|
||||
if (oemu == NULL) {
|
||||
err("calloc failed:");
|
||||
return -1;
|
||||
}
|
||||
|
||||
extend_set(&emu->ext, 'O', oemu);
|
||||
|
||||
if (mark_create(emu) != 0) {
|
||||
err("mark_create failed");
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -191,6 +210,11 @@ model_ovni_connect(struct emu *emu)
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (mark_connect(emu) != 0) {
|
||||
err("mark_connect failed");
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -23,7 +23,7 @@ emit(struct model *model, struct player *player)
|
||||
struct emu_ev *ev = player_ev(player);
|
||||
struct stream *stream = player_stream(player);
|
||||
|
||||
printf("%10ld %c%c%c %s ",
|
||||
printf("%10" PRIi64 " %c%c%c %s ",
|
||||
ev->rclock,
|
||||
ev->m,
|
||||
ev->c,
|
||||
|
@ -20,7 +20,7 @@ static int
|
||||
html_encode(char *dst, int ndst, const char *src)
|
||||
{
|
||||
int j = 0;
|
||||
int nsrc = strlen(src);
|
||||
int nsrc = (int) strlen(src);
|
||||
|
||||
for (int i = 0; i < nsrc; i++) {
|
||||
/* Simple check */
|
||||
@ -29,7 +29,7 @@ html_encode(char *dst, int ndst, const char *src)
|
||||
return -1;
|
||||
}
|
||||
|
||||
int c = src[i];
|
||||
char c = src[i];
|
||||
switch (c) {
|
||||
case '&': strcpy(&dst[j], "&"); j += 5; break;
|
||||
case '"': strcpy(&dst[j], """); j += 6; break;
|
||||
@ -87,7 +87,7 @@ print_model(struct model_spec *spec)
|
||||
printf("<dl>\n");
|
||||
for (long j = 0; j < spec->evspec->nevents; j++) {
|
||||
if (print_event(spec, j) != 0) {
|
||||
err("cannot print event %d", j);
|
||||
err("cannot print event %ld", j);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
@ -1,4 +1,4 @@
|
||||
/* Copyright (c) 2021-2023 Barcelona Supercomputing Center (BSC)
|
||||
/* Copyright (c) 2021-2024 Barcelona Supercomputing Center (BSC)
|
||||
* SPDX-License-Identifier: GPL-3.0-or-later */
|
||||
|
||||
/* This program is a really bad idea. It attempts to sort streams by using a
|
||||
@ -74,6 +74,20 @@ ring_add(struct ring *r, struct ovni_ev *ev)
|
||||
r->head = 0;
|
||||
}
|
||||
|
||||
static void
|
||||
ring_check(struct ring *r, long long start)
|
||||
{
|
||||
uint64_t last_clock = 0;
|
||||
for (long long i = start; i != r->tail; i = (i + 1) % r->size) {
|
||||
uint64_t clock = r->ev[i]->header.clock;
|
||||
if (clock < last_clock) {
|
||||
die("ring not sorted at i=%lld, last_clock=%"PRIu64" clock=%"PRIu64 ,
|
||||
i, last_clock, clock);
|
||||
}
|
||||
last_clock = clock;
|
||||
}
|
||||
}
|
||||
|
||||
static ssize_t
|
||||
find_destination(struct ring *r, uint64_t clock)
|
||||
{
|
||||
@ -86,7 +100,7 @@ find_destination(struct ring *r, uint64_t clock)
|
||||
for (ssize_t i = start; i != end; i = i - 1 < 0 ? r->size - 1 : i - 1) {
|
||||
last_clock = r->ev[i]->header.clock;
|
||||
if (last_clock < clock) {
|
||||
dbg("found suitable position %ld events backwards",
|
||||
dbg("found suitable position %zd events backwards",
|
||||
nback);
|
||||
return i;
|
||||
}
|
||||
@ -100,14 +114,14 @@ find_destination(struct ring *r, uint64_t clock)
|
||||
if (r->head != 0)
|
||||
die("ring head expected to be 0");
|
||||
if (r->tail >= r->size - 1)
|
||||
die("ring tail=%ld expected to be less than %ld", r->tail, r->size - 1);
|
||||
die("ring tail=%zd expected to be less than %zd", r->tail, r->size - 1);
|
||||
|
||||
dbg("starting of ring with nback=%ld", nback);
|
||||
dbg("starting of ring with nback=%zd", nback);
|
||||
return r->head;
|
||||
}
|
||||
|
||||
err("cannot find a event previous to clock %lu", clock);
|
||||
err("nback=%ld, last_clock=%lu", nback, last_clock);
|
||||
err("cannot find a event previous to clock %"PRIu64, clock);
|
||||
err("nback=%zd, last_clock=%"PRIu64, nback, last_clock);
|
||||
|
||||
return -1;
|
||||
}
|
||||
@ -179,11 +193,11 @@ write_events(struct ovni_ev **table, long n, uint8_t *buf)
|
||||
{
|
||||
for (long i = 0; i < n; i++) {
|
||||
struct ovni_ev *ev = table[i];
|
||||
size_t size = ovni_ev_size(ev);
|
||||
size_t size = (size_t) ovni_ev_size(ev);
|
||||
memcpy(buf, ev, size);
|
||||
buf += size;
|
||||
|
||||
dbg("injected event %c%c%c at %ld",
|
||||
dbg("injected event %c%c%c at %"PRIu64,
|
||||
ev->header.model,
|
||||
ev->header.category,
|
||||
ev->header.value,
|
||||
@ -200,8 +214,8 @@ cmp_ev(const void *a, const void *b)
|
||||
struct ovni_ev *ev1 = *pev1;
|
||||
struct ovni_ev *ev2 = *pev2;
|
||||
|
||||
int64_t clock1 = ev1->header.clock;
|
||||
int64_t clock2 = ev2->header.clock;
|
||||
int64_t clock1 = (int64_t) ev1->header.clock;
|
||||
int64_t clock2 = (int64_t) ev2->header.clock;
|
||||
|
||||
if (clock1 < clock2)
|
||||
return -1;
|
||||
@ -216,29 +230,29 @@ sort_buf(uint8_t *src, uint8_t *buf, int64_t bufsize)
|
||||
{
|
||||
struct ovni_ev *ev = (struct ovni_ev *) src;
|
||||
|
||||
dbg("first event before sorting %c%c%c at %ld",
|
||||
dbg("first event before sorting %c%c%c at %"PRIu64,
|
||||
ev->header.model,
|
||||
ev->header.category,
|
||||
ev->header.value,
|
||||
ev->header.clock);
|
||||
|
||||
/* Create a copy of the array */
|
||||
uint8_t *buf2 = malloc(bufsize);
|
||||
uint8_t *buf2 = malloc((size_t) bufsize);
|
||||
if (buf2 == NULL)
|
||||
die("malloc failed:");
|
||||
|
||||
memcpy(buf2, src, bufsize);
|
||||
memcpy(buf2, src, (size_t) bufsize);
|
||||
|
||||
long n = count_events(buf2, buf2 + bufsize);
|
||||
struct ovni_ev **table = calloc(n, sizeof(struct ovni_ev *));
|
||||
struct ovni_ev **table = calloc((size_t) n, sizeof(struct ovni_ev *));
|
||||
if (table == NULL)
|
||||
die("calloc failed:");
|
||||
|
||||
index_events(table, n, buf2);
|
||||
qsort(table, n, sizeof(struct ovni_ev *), cmp_ev);
|
||||
qsort(table, (size_t) n, sizeof(struct ovni_ev *), cmp_ev);
|
||||
write_events(table, n, buf);
|
||||
|
||||
dbg("first event after sorting %c%c%c at %ld",
|
||||
dbg("first event after sorting %c%c%c at %"PRIu64,
|
||||
ev->header.model,
|
||||
ev->header.category,
|
||||
ev->header.value,
|
||||
@ -254,44 +268,71 @@ static void
|
||||
write_stream(int fd, void *base, void *dst, const void *src, size_t size)
|
||||
{
|
||||
while (size > 0) {
|
||||
off_t offset = (off_t) ((int64_t) dst - (int64_t) base);
|
||||
off_t offset = (off_t) dst - (off_t) base;
|
||||
ssize_t written = pwrite(fd, src, size, offset);
|
||||
|
||||
if (written < 0)
|
||||
die("pwrite failed:");
|
||||
|
||||
size -= written;
|
||||
size -= (size_t) written;
|
||||
src = (void *) (((uint8_t *) src) + written);
|
||||
dst = (void *) (((uint8_t *) dst) + written);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
rebuild_ring(struct ring *r, long long start, struct ovni_ev *first, struct ovni_ev *last)
|
||||
{
|
||||
long long nbad = 0;
|
||||
long long n = 0;
|
||||
struct ovni_ev *ev = first;
|
||||
|
||||
for (long long i = start; i != r->tail; i = i + 1 >= r->size ? 0 : i + 1) {
|
||||
n++;
|
||||
if (ev != r->ev[i])
|
||||
nbad++;
|
||||
|
||||
if (ev >= last)
|
||||
die("exceeding last pointer");
|
||||
|
||||
r->ev[i] = ev;
|
||||
size_t size = (size_t) ovni_ev_size(ev);
|
||||
ev = (struct ovni_ev *) (((uint8_t *) ev) + size);
|
||||
}
|
||||
|
||||
if (ev != last)
|
||||
die("inconsistency: ev != last");
|
||||
|
||||
dbg("rebuilt ring with %lld / %lld misplaced events", nbad, n);
|
||||
}
|
||||
|
||||
static int
|
||||
execute_sort_plan(struct sortplan *sp)
|
||||
{
|
||||
uint64_t clock0 = sp->bad0->header.clock;
|
||||
dbg("attempt to sort: start clock %ld", sp->bad0->header.clock);
|
||||
dbg("attempt to sort: start clock %"PRIi64, sp->bad0->header.clock);
|
||||
|
||||
uint64_t min_clock = find_min_clock((void *) sp->bad0, (void *) sp->next);
|
||||
|
||||
if (min_clock < clock0) {
|
||||
clock0 = min_clock;
|
||||
dbg("region not sorted, using min clock=%ld", clock0);
|
||||
dbg("region not sorted, using min clock=%"PRIi64, clock0);
|
||||
}
|
||||
|
||||
/* Cannot sort in one pass; just fail for now */
|
||||
int64_t i0 = find_destination(sp->r, clock0);
|
||||
if (i0 < 0) {
|
||||
err("cannot find destination for region starting at clock %ld", clock0);
|
||||
err("cannot find destination for region starting at clock %"PRIi64, clock0);
|
||||
err("consider increasing the look back size with -n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Set the pointer to the first event that may be affected */
|
||||
struct ovni_ev *first = sp->r->ev[i0];
|
||||
long long dirty = i0;
|
||||
|
||||
/* Allocate a working buffer */
|
||||
int64_t bufsize = ((int64_t) sp->next) - ((int64_t) first);
|
||||
uintptr_t bufsize = (uintptr_t) sp->next - (uintptr_t) first;
|
||||
|
||||
if (bufsize <= 0)
|
||||
die("bufsize is non-positive");
|
||||
@ -300,12 +341,19 @@ execute_sort_plan(struct sortplan *sp)
|
||||
if (!buf)
|
||||
die("malloc failed:");
|
||||
|
||||
sort_buf((uint8_t *) first, buf, bufsize);
|
||||
sort_buf((uint8_t *) first, buf, (int64_t) bufsize);
|
||||
|
||||
write_stream(sp->fd, sp->base, first, buf, bufsize);
|
||||
|
||||
free(buf);
|
||||
|
||||
/* Pointers from the ring buffer are invalid now, rebuild them */
|
||||
rebuild_ring(sp->r, dirty, first, sp->next);
|
||||
|
||||
/* Invariant: The ring buffer is always sorted here. Check from the
|
||||
* dirty position onwards, so we avoid scanning all events. */
|
||||
ring_check(sp->r, dirty);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -313,7 +361,7 @@ execute_sort_plan(struct sortplan *sp)
|
||||
static int
|
||||
stream_winsort(struct stream *stream, struct ring *r)
|
||||
{
|
||||
char *fn = stream->path;
|
||||
char *fn = stream->obspath;
|
||||
int fd = open(fn, O_WRONLY);
|
||||
|
||||
if (fd < 0)
|
||||
@ -375,7 +423,7 @@ stream_winsort(struct stream *stream, struct ring *r)
|
||||
}
|
||||
|
||||
if (empty_regions > 0)
|
||||
warn("stream %s contains %ld empty sort regions",
|
||||
warn("stream %s contains %zd empty sort regions",
|
||||
stream->relpath, empty_regions);
|
||||
|
||||
if (updated && fdatasync(fd) < 0)
|
||||
@ -410,7 +458,7 @@ stream_check(struct stream *stream)
|
||||
uint64_t cur_clock = ovni_ev_get_clock(ev);
|
||||
|
||||
if (cur_clock < last_clock) {
|
||||
err("backwards jump in time %ld -> %ld for stream %s",
|
||||
err("backwards jump in time %"PRIi64" -> %"PRIi64" for stream %s",
|
||||
last_clock, cur_clock, stream->relpath);
|
||||
backjump = 1;
|
||||
}
|
||||
@ -435,8 +483,8 @@ process_trace(struct trace *trace)
|
||||
struct ring ring;
|
||||
int ret = 0;
|
||||
|
||||
ring.size = max_look_back;
|
||||
ring.ev = malloc(ring.size * sizeof(struct ovni_ev *));
|
||||
ring.size = (ssize_t) max_look_back;
|
||||
ring.ev = malloc((size_t) ring.size * sizeof(struct ovni_ev *));
|
||||
|
||||
if (ring.ev == NULL)
|
||||
die("malloc failed:");
|
||||
@ -483,7 +531,7 @@ usage(void)
|
||||
rerr("Sorts the events in each stream of the trace given in\n");
|
||||
rerr("tracedir, so they are suitable for the emulator ovniemu.\n");
|
||||
rerr("Only the events enclosed by OU[ OU] are sorted. At most a\n");
|
||||
rerr("total of %ld events are looked back to insert the unsorted\n",
|
||||
rerr("total of %zd events are looked back to insert the unsorted\n",
|
||||
max_look_back);
|
||||
rerr("events, so the sort procedure can fail with an error.\n");
|
||||
rerr("\n");
|
||||
@ -492,7 +540,7 @@ usage(void)
|
||||
rerr(" trace is already sorted.\n");
|
||||
rerr("\n");
|
||||
rerr(" -n Set the number of events to look back.\n");
|
||||
rerr(" Defaul: %ld\n", max_look_back);
|
||||
rerr(" Default: %zd\n", max_look_back);
|
||||
rerr("\n");
|
||||
rerr(" tracedir The trace directory generated by ovni.\n");
|
||||
rerr("\n");
|
||||
@ -511,7 +559,7 @@ parse_args(int argc, char *argv[])
|
||||
operation_mode = CHECK;
|
||||
break;
|
||||
case 'n':
|
||||
max_look_back = atol(optarg);
|
||||
max_look_back = (size_t) atol(optarg);
|
||||
break;
|
||||
default: /* '?' */
|
||||
usage();
|
||||
|
@ -1,4 +1,4 @@
|
||||
/* Copyright (c) 2021-2023 Barcelona Supercomputing Center (BSC)
|
||||
/* Copyright (c) 2021-2024 Barcelona Supercomputing Center (BSC)
|
||||
* SPDX-License-Identifier: GPL-3.0-or-later */
|
||||
|
||||
#include <errno.h>
|
||||
@ -182,7 +182,7 @@ fill_offset(struct offset *offset, int nsamples)
|
||||
static void
|
||||
offset_compute_delta(struct offset *ref, struct offset *cur, int nsamples, int verbose)
|
||||
{
|
||||
double *delta = malloc(sizeof(double) * nsamples);
|
||||
double *delta = malloc(sizeof(double) * (size_t) nsamples);
|
||||
|
||||
if (delta == NULL) {
|
||||
perror("malloc");
|
||||
@ -199,7 +199,7 @@ offset_compute_delta(struct offset *ref, struct offset *cur, int nsamples, int v
|
||||
}
|
||||
}
|
||||
|
||||
qsort(delta, nsamples, sizeof(double), cmp_double);
|
||||
qsort(delta, (size_t) nsamples, sizeof(double), cmp_double);
|
||||
|
||||
cur->delta_median = delta[nsamples / 2];
|
||||
cur->delta_mean = 0;
|
||||
@ -223,14 +223,14 @@ offset_compute_delta(struct offset *ref, struct offset *cur, int nsamples, int v
|
||||
static size_t
|
||||
offset_size(int nsamples)
|
||||
{
|
||||
return sizeof(struct offset) + sizeof(double) * nsamples;
|
||||
return sizeof(struct offset) + sizeof(double) * (size_t) nsamples;
|
||||
}
|
||||
|
||||
static struct offset *
|
||||
table_get_offset(struct offset_table *table, int i, int nsamples)
|
||||
{
|
||||
char *p = (char *) table->_offset;
|
||||
p += i * offset_size(nsamples);
|
||||
p += (size_t) i * offset_size(nsamples);
|
||||
|
||||
return (struct offset *) p;
|
||||
}
|
||||
@ -252,14 +252,14 @@ build_offset_table(int nsamples, int rank, int verbose)
|
||||
|
||||
MPI_Comm_size(MPI_COMM_WORLD, &table->nprocs);
|
||||
|
||||
table->_offset = calloc(table->nprocs, offset_size(nsamples));
|
||||
table->_offset = calloc((size_t) table->nprocs, offset_size(nsamples));
|
||||
|
||||
if (table->_offset == NULL) {
|
||||
perror("malloc");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
table->offset = malloc(sizeof(struct offset *) * table->nprocs);
|
||||
table->offset = malloc(sizeof(struct offset *) * (size_t) table->nprocs);
|
||||
|
||||
if (table->offset == NULL) {
|
||||
perror("malloc");
|
||||
@ -288,8 +288,8 @@ build_offset_table(int nsamples, int rank, int verbose)
|
||||
void *sendbuf = rank == 0 ? MPI_IN_PLACE : offset;
|
||||
|
||||
/* Then collect all the offsets into the rank 0 */
|
||||
MPI_Gather(sendbuf, offset_size(nsamples), MPI_CHAR,
|
||||
offset, offset_size(nsamples), MPI_CHAR,
|
||||
MPI_Gather(sendbuf, (int) offset_size(nsamples), MPI_CHAR,
|
||||
offset, (int) offset_size(nsamples), MPI_CHAR,
|
||||
0, MPI_COMM_WORLD);
|
||||
|
||||
/* Finish the offsets by computing the deltas on rank 0 */
|
||||
@ -399,7 +399,7 @@ do_work(struct options *options, int rank)
|
||||
}
|
||||
|
||||
if (drift_mode)
|
||||
sleep(options->drift_wait);
|
||||
sleep((unsigned) options->drift_wait);
|
||||
}
|
||||
|
||||
if (rank == 0)
|
||||
|
@ -1,4 +1,4 @@
|
||||
/* Copyright (c) 2021-2023 Barcelona Supercomputing Center (BSC)
|
||||
/* Copyright (c) 2021-2024 Barcelona Supercomputing Center (BSC)
|
||||
* SPDX-License-Identifier: GPL-3.0-or-later */
|
||||
|
||||
#include "path.h"
|
||||
@ -95,7 +95,7 @@ path_keep(char *path, int n)
|
||||
void
|
||||
path_remove_trailing(char *path)
|
||||
{
|
||||
int n = strlen(path);
|
||||
int n = (int) strlen(path);
|
||||
for (int i = n - 1; i >= 0 && path[i] == '/'; i--) {
|
||||
path[i] = '\0';
|
||||
}
|
||||
@ -112,3 +112,47 @@ path_filename(const char *path)
|
||||
|
||||
return start;
|
||||
}
|
||||
|
||||
int
|
||||
path_append(char dst[PATH_MAX], const char *src, const char *extra)
|
||||
{
|
||||
if (snprintf(dst, PATH_MAX, "%s/%s", src, extra) >= PATH_MAX) {
|
||||
err("path too long: %s/%s", src, extra);
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/** Copy the path src into dst. */
|
||||
int
|
||||
path_copy(char dst[PATH_MAX], const char *src)
|
||||
{
|
||||
if (snprintf(dst, PATH_MAX, "%s", src) >= PATH_MAX) {
|
||||
err("path too long: %s", src);
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/** Strip last component from path */
|
||||
void
|
||||
path_dirname(char path[PATH_MAX])
|
||||
{
|
||||
path_remove_trailing(path);
|
||||
int n = (int) strlen(path);
|
||||
int i;
|
||||
for (i = n - 1; i >= 0; i--) {
|
||||
if (path[i] == '/') {
|
||||
break;
|
||||
}
|
||||
}
|
||||
/* Remove all '/' */
|
||||
for (; i >= 0; i--) {
|
||||
if (path[i] != '/')
|
||||
break;
|
||||
else
|
||||
path[i] = '\0';
|
||||
}
|
||||
}
|
||||
|
@ -1,9 +1,10 @@
|
||||
/* Copyright (c) 2021-2023 Barcelona Supercomputing Center (BSC)
|
||||
/* Copyright (c) 2021-2024 Barcelona Supercomputing Center (BSC)
|
||||
* SPDX-License-Identifier: GPL-3.0-or-later */
|
||||
|
||||
#ifndef PATH_H
|
||||
#define PATH_H
|
||||
|
||||
#include <limits.h>
|
||||
#include "common.h"
|
||||
|
||||
USE_RET int path_has_prefix(const char *path, const char *prefix);
|
||||
@ -13,5 +14,8 @@ USE_RET int path_keep(char *path, int n);
|
||||
USE_RET int path_strip(const char *path, int n, const char (**next));
|
||||
void path_remove_trailing(char *path);
|
||||
USE_RET const char *path_filename(const char *path);
|
||||
USE_RET int path_append(char dst[PATH_MAX], const char *src, const char *extra);
|
||||
USE_RET int path_copy(char dst[PATH_MAX], const char *src);
|
||||
void path_dirname(char path[PATH_MAX]);
|
||||
|
||||
#endif /* PATH_H */
|
||||
|
@ -1,4 +1,4 @@
|
||||
/* Copyright (c) 2021-2023 Barcelona Supercomputing Center (BSC)
|
||||
/* Copyright (c) 2021-2024 Barcelona Supercomputing Center (BSC)
|
||||
* SPDX-License-Identifier: GPL-3.0-or-later */
|
||||
|
||||
#include "player.h"
|
||||
@ -164,7 +164,7 @@ update_clocks(struct player *player, struct stream *stream)
|
||||
}
|
||||
|
||||
if (sclock < player->lastclock) {
|
||||
err("backwards jump in time %ld -> %ld in stream '%s'",
|
||||
err("backwards jump in time %"PRIi64" -> %"PRIi64" in stream '%s'",
|
||||
player->lastclock, sclock, stream->relpath);
|
||||
if (player->unsorted == 0)
|
||||
return -1;
|
||||
|
186
src/emu/proc.c
186
src/emu/proc.c
@ -1,4 +1,4 @@
|
||||
/* Copyright (c) 2021-2023 Barcelona Supercomputing Center (BSC)
|
||||
/* Copyright (c) 2021-2024 Barcelona Supercomputing Center (BSC)
|
||||
* SPDX-License-Identifier: GPL-3.0-or-later */
|
||||
|
||||
#include "proc.h"
|
||||
@ -6,85 +6,42 @@
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include "path.h"
|
||||
#include "stream.h"
|
||||
#include "thread.h"
|
||||
|
||||
static int
|
||||
get_pid(const char *id, int *pid)
|
||||
int
|
||||
proc_stream_get_pid(struct stream *s)
|
||||
{
|
||||
/* TODO: Store the PID the metadata.json instead */
|
||||
JSON_Object *meta = stream_metadata(s);
|
||||
|
||||
/* The id must be like "loom.host01.123/proc.345" */
|
||||
if (path_count(id, '/') != 1) {
|
||||
err("proc id can only contain one '/': %s", id);
|
||||
double pid = json_object_dotget_number(meta, "ovni.pid");
|
||||
|
||||
/* Zero is used for errors, so forbidden for pid too */
|
||||
if (pid == 0) {
|
||||
err("cannot get attribute ovni.pid for stream: %s",
|
||||
s->relpath);
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Get the proc.345 part */
|
||||
const char *procname;
|
||||
if (path_next(id, '/', &procname) != 0) {
|
||||
err("cannot get proc name");
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Ensure the prefix is ok */
|
||||
const char prefix[] = "proc.";
|
||||
if (!path_has_prefix(procname, prefix)) {
|
||||
err("proc name must start with '%s': %s", prefix, id);
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Get the 345 part */
|
||||
const char *pidstr;
|
||||
if (path_next(procname, '.', &pidstr) != 0) {
|
||||
err("cannot find proc dot in '%s'", id);
|
||||
return -1;
|
||||
}
|
||||
|
||||
*pid = atoi(pidstr);
|
||||
|
||||
return 0;
|
||||
return (int) pid;
|
||||
}
|
||||
|
||||
int
|
||||
proc_relpath_get_pid(const char *relpath, int *pid)
|
||||
{
|
||||
char id[PATH_MAX];
|
||||
|
||||
if (snprintf(id, PATH_MAX, "%s", relpath) >= PATH_MAX) {
|
||||
err("path too long");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (path_keep(id, 2) != 0) {
|
||||
err("cannot delimite proc dir");
|
||||
return -1;
|
||||
}
|
||||
|
||||
return get_pid(id, pid);
|
||||
}
|
||||
|
||||
int
|
||||
proc_init_begin(struct proc *proc, const char *relpath)
|
||||
proc_init_begin(struct proc *proc, int pid)
|
||||
{
|
||||
memset(proc, 0, sizeof(struct proc));
|
||||
|
||||
proc->gindex = -1;
|
||||
proc->appid = 0;
|
||||
proc->rank = -1;
|
||||
proc->nranks = 0;
|
||||
proc->pid = pid;
|
||||
|
||||
if (snprintf(proc->id, PATH_MAX, "%s", relpath) >= PATH_MAX) {
|
||||
if (snprintf(proc->id, PATH_MAX, "proc.%d", pid) >= PATH_MAX) {
|
||||
err("path too long");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (path_keep(proc->id, 2) != 0) {
|
||||
err("cannot delimite proc dir");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (get_pid(proc->id, &proc->pid) != 0) {
|
||||
err("cannot parse proc pid");
|
||||
return -1;
|
||||
}
|
||||
|
||||
dbg("created proc %s", proc->id);
|
||||
|
||||
return 0;
|
||||
@ -102,38 +59,105 @@ proc_set_loom(struct proc *proc, struct loom *loom)
|
||||
proc->loom = loom;
|
||||
}
|
||||
|
||||
int
|
||||
proc_load_metadata(struct proc *proc, JSON_Object *meta)
|
||||
static int
|
||||
load_appid(struct proc *proc, struct stream *s)
|
||||
{
|
||||
if (proc->metadata_loaded) {
|
||||
err("process %s already loaded metadata", proc->id);
|
||||
JSON_Object *meta = stream_metadata(s);
|
||||
JSON_Value *appid_val = json_object_dotget_value(meta, "ovni.app_id");
|
||||
|
||||
/* May not be present in all thread streams */
|
||||
if (appid_val == NULL)
|
||||
return 0;
|
||||
|
||||
int appid = (int) json_number(appid_val);
|
||||
if (proc->appid && proc->appid != appid) {
|
||||
err("mismatch previous appid %d with stream: %s",
|
||||
proc->appid, s->relpath);
|
||||
return -1;
|
||||
}
|
||||
|
||||
JSON_Value *version_val = json_object_get_value(meta, "version");
|
||||
if (version_val == NULL) {
|
||||
err("missing attribute 'version' in metadata");
|
||||
if (appid <= 0) {
|
||||
err("appid must be >0, stream: %s", s->relpath);
|
||||
return -1;
|
||||
}
|
||||
|
||||
proc->metadata_version = (int) json_number(version_val);
|
||||
proc->appid = appid;
|
||||
return 0;
|
||||
}
|
||||
|
||||
JSON_Value *appid_val = json_object_get_value(meta, "app_id");
|
||||
if (appid_val == NULL) {
|
||||
err("missing attribute 'app_id' in metadata");
|
||||
static int
|
||||
load_rank(struct proc *proc, struct stream *s)
|
||||
{
|
||||
JSON_Object *meta = stream_metadata(s);
|
||||
JSON_Value *rank_val = json_object_dotget_value(meta, "ovni.rank");
|
||||
|
||||
/* Optional */
|
||||
if (rank_val == NULL) {
|
||||
dbg("process %s has no rank", proc->id);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int rank = (int) json_number(rank_val);
|
||||
|
||||
if (rank < 0) {
|
||||
err("rank %d must be >=0, stream: %s", rank, s->relpath);
|
||||
return -1;
|
||||
}
|
||||
|
||||
proc->appid = (int) json_number(appid_val);
|
||||
if (proc->rank >= 0 && proc->rank != rank) {
|
||||
err("mismatch previous rank %d with stream: %s",
|
||||
proc->rank, s->relpath);
|
||||
return -1;
|
||||
}
|
||||
|
||||
JSON_Value *rank_val = json_object_get_value(meta, "rank");
|
||||
/* Same with nranks, but it is not optional now */
|
||||
JSON_Value *nranks_val = json_object_dotget_value(meta, "ovni.nranks");
|
||||
if (nranks_val == NULL) {
|
||||
err("missing ovni.nranks attribute: %s", s->relpath);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (rank_val != NULL)
|
||||
proc->rank = (int) json_number(rank_val);
|
||||
else
|
||||
proc->rank = -1;
|
||||
int nranks = (int) json_number(nranks_val);
|
||||
|
||||
proc->metadata_loaded = 1;
|
||||
if (nranks <= 0) {
|
||||
err("nranks %d must be >0, stream: %s", nranks, s->relpath);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (proc->nranks > 0 && proc->nranks != nranks) {
|
||||
err("mismatch previous nranks %d with stream: %s",
|
||||
proc->nranks, s->relpath);
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Ensure rank fits in nranks */
|
||||
if (rank >= nranks) {
|
||||
err("rank %d must be lower than nranks %d: %s",
|
||||
rank, nranks, s->relpath);
|
||||
return -1;
|
||||
}
|
||||
|
||||
dbg("process %s rank=%d nranks=%d",
|
||||
proc->id, rank, nranks);
|
||||
proc->rank = rank;
|
||||
proc->nranks = nranks;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/** Merges the metadata from the stream in the process. */
|
||||
int
|
||||
proc_load_metadata(struct proc *proc, struct stream *s)
|
||||
{
|
||||
if (load_appid(proc, s) != 0) {
|
||||
err("load_appid failed for stream: %s", s->relpath);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (load_rank(proc, s) != 0) {
|
||||
err("load_rank failed for stream: %s", s->relpath);
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
@ -197,8 +221,8 @@ proc_init_end(struct proc *proc)
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (!proc->metadata_loaded) {
|
||||
err("metadata not loaded");
|
||||
if (proc->appid <= 0) {
|
||||
err("appid not set");
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
@ -1,4 +1,4 @@
|
||||
/* Copyright (c) 2021-2023 Barcelona Supercomputing Center (BSC)
|
||||
/* Copyright (c) 2021-2024 Barcelona Supercomputing Center (BSC)
|
||||
* SPDX-License-Identifier: GPL-3.0-or-later */
|
||||
|
||||
#ifndef PROC_H
|
||||
@ -8,9 +8,9 @@
|
||||
#include <stdint.h>
|
||||
#include "common.h"
|
||||
#include "extend.h"
|
||||
#include "parson.h"
|
||||
#include "uthash.h"
|
||||
struct loom;
|
||||
struct stream;
|
||||
struct thread;
|
||||
|
||||
struct proc {
|
||||
@ -18,12 +18,11 @@ struct proc {
|
||||
char id[PATH_MAX];
|
||||
int is_init;
|
||||
|
||||
int metadata_loaded;
|
||||
int metadata_version;
|
||||
int pid;
|
||||
int index;
|
||||
int appid;
|
||||
int rank;
|
||||
int nranks;
|
||||
|
||||
int nthreads;
|
||||
struct thread *threads;
|
||||
@ -45,14 +44,14 @@ struct proc {
|
||||
struct extend ext;
|
||||
};
|
||||
|
||||
USE_RET int proc_relpath_get_pid(const char *relpath, int *pid);
|
||||
USE_RET int proc_init_begin(struct proc *proc, const char *id);
|
||||
USE_RET int proc_stream_get_pid(struct stream *s);
|
||||
USE_RET int proc_init_begin(struct proc *proc, int pid);
|
||||
USE_RET int proc_init_end(struct proc *proc);
|
||||
USE_RET int proc_get_pid(struct proc *proc);
|
||||
void proc_set_gindex(struct proc *proc, int64_t gindex);
|
||||
void proc_set_loom(struct proc *proc, struct loom *loom);
|
||||
void proc_sort(struct proc *proc);
|
||||
USE_RET int proc_load_metadata(struct proc *proc, JSON_Object *meta);
|
||||
USE_RET int proc_load_metadata(struct proc *proc, struct stream *s);
|
||||
USE_RET struct thread *proc_find_thread(struct proc *proc, int tid);
|
||||
USE_RET int proc_add_thread(struct proc *proc, struct thread *thread);
|
||||
void proc_sort(struct proc *proc);
|
||||
|
@ -1,4 +1,4 @@
|
||||
/* Copyright (c) 2021-2023 Barcelona Supercomputing Center (BSC)
|
||||
/* Copyright (c) 2021-2024 Barcelona Supercomputing Center (BSC)
|
||||
* SPDX-License-Identifier: GPL-3.0-or-later */
|
||||
|
||||
#include "prf.h"
|
||||
@ -20,7 +20,7 @@ prf_open(struct prf *prf, const char *path, long nrows)
|
||||
}
|
||||
|
||||
prf->nrows = nrows;
|
||||
prf->rows = calloc(nrows, sizeof(struct prf_row));
|
||||
prf->rows = calloc((size_t) nrows, sizeof(struct prf_row));
|
||||
|
||||
if (prf->rows == NULL) {
|
||||
err("calloc failed:");
|
||||
|
@ -1,4 +1,4 @@
|
||||
/* Copyright (c) 2021-2023 Barcelona Supercomputing Center (BSC)
|
||||
/* Copyright (c) 2021-2024 Barcelona Supercomputing Center (BSC)
|
||||
* SPDX-License-Identifier: GPL-3.0-or-later */
|
||||
|
||||
#include "prv.h"
|
||||
@ -25,7 +25,7 @@ prv_open_file(struct prv *prv, long nrows, FILE *file)
|
||||
prv->file = file;
|
||||
|
||||
/* Write fake header to allocate the space */
|
||||
write_header(file, 0LL, nrows);
|
||||
write_header(file, 0LL, (int) nrows);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@ -48,7 +48,7 @@ prv_close(struct prv *prv)
|
||||
{
|
||||
/* Fix the header with the current duration */
|
||||
fseek(prv->file, 0, SEEK_SET);
|
||||
write_header(prv->file, prv->time, prv->nrows);
|
||||
write_header(prv->file, prv->time, (int) prv->nrows);
|
||||
fclose(prv->file);
|
||||
return 0;
|
||||
}
|
||||
@ -69,9 +69,9 @@ find_prv_chan(struct prv *prv, long id)
|
||||
}
|
||||
|
||||
static void
|
||||
write_line(struct prv *prv, long row_base1, long type, long value)
|
||||
write_line(struct prv *prv, long row_base1, int64_t type, int64_t value)
|
||||
{
|
||||
fprintf(prv->file, "2:0:1:1:%ld:%ld:%ld:%ld\n",
|
||||
fprintf(prv->file, "2:0:1:1:%ld:%"PRIi64":%"PRIi64":%"PRIi64"\n",
|
||||
row_base1, prv->time, type, value);
|
||||
}
|
||||
|
||||
@ -117,7 +117,7 @@ emit(struct prv *prv, struct prv_chan *rchan)
|
||||
}
|
||||
|
||||
/* Assume null */
|
||||
long val = 0;
|
||||
int64_t val = 0;
|
||||
if (likely(value.type == VALUE_INT64)) {
|
||||
val = value.i;
|
||||
if (rchan->flags & PRV_NEXT)
|
||||
|
@ -1,4 +1,4 @@
|
||||
/* Copyright (c) 2023 Barcelona Supercomputing Center (BSC)
|
||||
/* Copyright (c) 2023-2024 Barcelona Supercomputing Center (BSC)
|
||||
* SPDX-License-Identifier: GPL-3.0-or-later */
|
||||
|
||||
#include "sort.h"
|
||||
@ -106,8 +106,8 @@ sort_cb_input(struct chan *in_chan, void *ptr)
|
||||
if (likely(sort->copied)) {
|
||||
sort_replace(sort->sorted, sort->n, old, new);
|
||||
} else {
|
||||
memcpy(sort->sorted, sort->values, sort->n * sizeof(int64_t));
|
||||
qsort(sort->sorted, sort->n, sizeof(int64_t), cmp_int64);
|
||||
memcpy(sort->sorted, sort->values, (size_t) sort->n * sizeof(int64_t));
|
||||
qsort(sort->sorted, (size_t) sort->n, sizeof(int64_t), cmp_int64);
|
||||
sort->copied = 1;
|
||||
}
|
||||
|
||||
@ -141,22 +141,22 @@ sort_init(struct sort *sort, struct bay *bay, int64_t n, const char *name)
|
||||
memset(sort, 0, sizeof(struct sort));
|
||||
sort->bay = bay;
|
||||
sort->n = n;
|
||||
sort->inputs = calloc(n, sizeof(struct sort_input));
|
||||
sort->inputs = calloc((size_t) n, sizeof(struct sort_input));
|
||||
if (sort->inputs == NULL) {
|
||||
err("calloc failed:");
|
||||
return -1;
|
||||
}
|
||||
sort->outputs = calloc(n, sizeof(struct chan));
|
||||
sort->outputs = calloc((size_t) n, sizeof(struct chan));
|
||||
if (sort->outputs == NULL) {
|
||||
err("calloc failed:");
|
||||
return -1;
|
||||
}
|
||||
sort->values = calloc(n, sizeof(int64_t));
|
||||
sort->values = calloc((size_t) n, sizeof(int64_t));
|
||||
if (sort->values == NULL) {
|
||||
err("calloc failed:");
|
||||
return -1;
|
||||
}
|
||||
sort->sorted = calloc(n, sizeof(int64_t));
|
||||
sort->sorted = calloc((size_t) n, sizeof(int64_t));
|
||||
if (sort->sorted == NULL) {
|
||||
err("calloc failed:");
|
||||
return -1;
|
||||
@ -165,7 +165,7 @@ sort_init(struct sort *sort, struct bay *bay, int64_t n, const char *name)
|
||||
/* Init and register outputs */
|
||||
for (int64_t i = 0; i < n; i++) {
|
||||
struct chan *out = &sort->outputs[i];
|
||||
chan_init(out, CHAN_SINGLE, "%s.out%ld", name, i);
|
||||
chan_init(out, CHAN_SINGLE, "%s.out%"PRIi64, name, i);
|
||||
|
||||
/* The sort module may write multiple times to the same
|
||||
* channel if we update more than one input. */
|
||||
@ -176,7 +176,7 @@ sort_init(struct sort *sort, struct bay *bay, int64_t n, const char *name)
|
||||
chan_prop_set(out, CHAN_ALLOW_DUP, 1);
|
||||
|
||||
if (bay_register(bay, out) != 0) {
|
||||
err("bay_register out%ld failed", i);
|
||||
err("bay_register out%"PRIi64" failed", i);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
@ -190,7 +190,7 @@ sort_set_input(struct sort *sort, int64_t index, struct chan *chan)
|
||||
struct sort_input *input = &sort->inputs[index];
|
||||
|
||||
if (input->chan != NULL) {
|
||||
err("input %d already has a channel", index);
|
||||
err("input %"PRIi64" already has a channel", index);
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
144
src/emu/stream.c
144
src/emu/stream.c
@ -1,4 +1,4 @@
|
||||
/* Copyright (c) 2021-2023 Barcelona Supercomputing Center (BSC)
|
||||
/* Copyright (c) 2021-2024 Barcelona Supercomputing Center (BSC)
|
||||
* SPDX-License-Identifier: GPL-3.0-or-later */
|
||||
|
||||
#include "stream.h"
|
||||
@ -59,7 +59,7 @@ load_stream_fd(struct stream *stream, int fd)
|
||||
}
|
||||
|
||||
int prot = PROT_READ | PROT_WRITE;
|
||||
stream->buf = mmap(NULL, st.st_size, prot, MAP_PRIVATE, fd, 0);
|
||||
stream->buf = mmap(NULL, (size_t) st.st_size, prot, MAP_PRIVATE, fd, 0);
|
||||
|
||||
if (stream->buf == MAP_FAILED) {
|
||||
err("mmap failed:");
|
||||
@ -71,6 +71,96 @@ load_stream_fd(struct stream *stream, int fd)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
load_obs(struct stream *stream, const char *path)
|
||||
{
|
||||
int fd;
|
||||
if ((fd = open(path, O_RDWR)) == -1) {
|
||||
err("open %s failed:", path);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (load_stream_fd(stream, fd) != 0) {
|
||||
err("load_stream_fd failed for: %s", path);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (check_stream_header(stream) != 0) {
|
||||
err("stream has bad header: %s", path);
|
||||
return -1;
|
||||
}
|
||||
|
||||
stream->offset = sizeof(struct ovni_stream_header);
|
||||
stream->usize = stream->size - stream->offset;
|
||||
|
||||
if (stream->offset < stream->size) {
|
||||
stream->active = 1;
|
||||
} else if (stream->offset == stream->size) {
|
||||
warn("stream '%s' has zero events", stream->relpath);
|
||||
stream->active = 0;
|
||||
} else {
|
||||
err("impossible, offset %"PRIi64" bigger than size %"PRIi64,
|
||||
stream->offset, stream->size);
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* No need to keep the fd open */
|
||||
if (close(fd)) {
|
||||
err("close failed:");
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
check_version(JSON_Object *meta)
|
||||
{
|
||||
JSON_Value *version_val = json_object_get_value(meta, "version");
|
||||
if (version_val == NULL) {
|
||||
err("missing attribute \"version\"");
|
||||
return -1;
|
||||
}
|
||||
|
||||
int version = (int) json_number(version_val);
|
||||
|
||||
if (version != OVNI_METADATA_VERSION) {
|
||||
err("metadata version mismatch %d (expected %d)",
|
||||
version, OVNI_METADATA_VERSION);
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static JSON_Object *
|
||||
load_json(const char *path)
|
||||
{
|
||||
JSON_Value *vmeta = json_parse_file_with_comments(path);
|
||||
if (vmeta == NULL) {
|
||||
err("json_parse_file_with_comments() failed");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
JSON_Object *meta = json_value_get_object(vmeta);
|
||||
if (meta == NULL) {
|
||||
err("json_value_get_object() failed");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (check_version(meta) != 0) {
|
||||
err("check_version failed");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return meta;
|
||||
}
|
||||
|
||||
/** Loads a stream from disk.
|
||||
*
|
||||
* The relpath must be pointing to a directory with the stream.json and
|
||||
* stream.obs files.
|
||||
*/
|
||||
int
|
||||
stream_load(struct stream *stream, const char *tracedir, const char *relpath)
|
||||
{
|
||||
@ -91,41 +181,23 @@ stream_load(struct stream *stream, const char *tracedir, const char *relpath)
|
||||
|
||||
dbg("loading %s", stream->relpath);
|
||||
|
||||
int fd;
|
||||
if ((fd = open(stream->path, O_RDWR)) == -1) {
|
||||
err("open %s failed:", stream->path);
|
||||
if (path_append(stream->jsonpath, stream->path, "stream.json") != 0) {
|
||||
err("path_append failed");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (load_stream_fd(stream, fd) != 0) {
|
||||
err("load_stream_fd failed for stream '%s'",
|
||||
stream->path);
|
||||
if ((stream->meta = load_json(stream->jsonpath)) == NULL) {
|
||||
err("load_json failed for: %s", stream->jsonpath);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (check_stream_header(stream) != 0) {
|
||||
err("stream '%s' has bad header",
|
||||
stream->path);
|
||||
if (path_append(stream->obspath, stream->path, "stream.obs") != 0) {
|
||||
err("path_append failed");
|
||||
return -1;
|
||||
}
|
||||
|
||||
stream->offset = sizeof(struct ovni_stream_header);
|
||||
stream->usize = stream->size - stream->offset;
|
||||
|
||||
if (stream->offset < stream->size) {
|
||||
stream->active = 1;
|
||||
} else if (stream->offset == stream->size) {
|
||||
warn("stream '%s' has zero events", stream->relpath);
|
||||
stream->active = 0;
|
||||
} else {
|
||||
err("impossible, offset %ld bigger than size %ld",
|
||||
stream->offset, stream->size);
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* No need to keep the fd open */
|
||||
if (close(fd)) {
|
||||
err("close failed:");
|
||||
if (load_obs(stream, stream->obspath) != 0) {
|
||||
err("load_obs failed");
|
||||
return -1;
|
||||
}
|
||||
|
||||
@ -144,6 +216,16 @@ stream_data_get(struct stream *stream)
|
||||
return stream->data;
|
||||
}
|
||||
|
||||
/* Is never NULL */
|
||||
JSON_Object *
|
||||
stream_metadata(struct stream *stream)
|
||||
{
|
||||
if (stream->meta == NULL)
|
||||
die("stream metadata is NULL: %s", stream->relpath);
|
||||
|
||||
return stream->meta;
|
||||
}
|
||||
|
||||
int
|
||||
stream_clkoff_set(struct stream *stream, int64_t clkoff)
|
||||
{
|
||||
@ -196,7 +278,7 @@ stream_step(struct stream *stream)
|
||||
|
||||
/* It cannot pass the size, otherwise we are reading garbage */
|
||||
if (stream->offset > stream->size) {
|
||||
err("stream offset %ld exceeds size %ld",
|
||||
err("stream offset %"PRIi64" exceeds size %"PRIi64,
|
||||
stream->offset, stream->size);
|
||||
return -1;
|
||||
}
|
||||
@ -223,7 +305,7 @@ stream_step(struct stream *stream)
|
||||
/* Ensure the clock grows monotonically if unsorted flag not set */
|
||||
if (stream->unsorted == 0) {
|
||||
if (clock < stream->lastclock) {
|
||||
err("clock goes backwards %ld -> %ld in stream '%s' at offset %ld",
|
||||
err("clock goes backwards %"PRIi64" -> %"PRIi64" in stream '%s' at offset %"PRIi64,
|
||||
stream->lastclock,
|
||||
clock,
|
||||
stream->relpath,
|
||||
@ -241,7 +323,7 @@ stream_step(struct stream *stream)
|
||||
void
|
||||
stream_progress(struct stream *stream, int64_t *done, int64_t *total)
|
||||
{
|
||||
*done = stream->offset - sizeof(struct ovni_stream_header);
|
||||
*done = stream->offset - (int64_t) sizeof(struct ovni_stream_header);
|
||||
*total = stream->usize;
|
||||
}
|
||||
|
||||
|
@ -1,4 +1,4 @@
|
||||
/* Copyright (c) 2021-2023 Barcelona Supercomputing Center (BSC)
|
||||
/* Copyright (c) 2021-2024 Barcelona Supercomputing Center (BSC)
|
||||
* SPDX-License-Identifier: GPL-3.0-or-later */
|
||||
|
||||
#ifndef STREAM_H
|
||||
@ -8,6 +8,7 @@
|
||||
#include <stdint.h>
|
||||
#include "common.h"
|
||||
#include "heap.h"
|
||||
#include "parson.h"
|
||||
struct ovni_ev;
|
||||
|
||||
struct stream {
|
||||
@ -27,13 +28,17 @@ struct stream {
|
||||
int active;
|
||||
int unsorted;
|
||||
|
||||
char path[PATH_MAX];
|
||||
char path[PATH_MAX]; /* To stream dir */
|
||||
char relpath[PATH_MAX]; /* To tracedir */
|
||||
char obspath[PATH_MAX]; /* To obs file */
|
||||
char jsonpath[PATH_MAX]; /* To json file */
|
||||
|
||||
int64_t usize; /* Useful size for events */
|
||||
int64_t offset;
|
||||
|
||||
double progress;
|
||||
|
||||
JSON_Object *meta;
|
||||
};
|
||||
|
||||
USE_RET int stream_load(struct stream *stream, const char *tracedir, const char *relpath);
|
||||
@ -46,5 +51,6 @@ USE_RET int64_t stream_lastclock(struct stream *stream);
|
||||
void stream_allow_unsorted(struct stream *stream);
|
||||
void stream_data_set(struct stream *stream, void *data);
|
||||
USE_RET void *stream_data_get(struct stream *stream);
|
||||
USE_RET JSON_Object *stream_metadata(struct stream *stream);
|
||||
|
||||
#endif /* STREAM_H */
|
||||
|
193
src/emu/system.c
193
src/emu/system.c
@ -1,4 +1,4 @@
|
||||
/* Copyright (c) 2021-2023 Barcelona Supercomputing Center (BSC)
|
||||
/* Copyright (c) 2021-2024 Barcelona Supercomputing Center (BSC)
|
||||
* SPDX-License-Identifier: GPL-3.0-or-later */
|
||||
|
||||
#include "system.h"
|
||||
@ -11,7 +11,6 @@
|
||||
#include "cpu.h"
|
||||
#include "emu_args.h"
|
||||
#include "loom.h"
|
||||
#include "metadata.h"
|
||||
#include "proc.h"
|
||||
#include "pv/prf.h"
|
||||
#include "pv/pvt.h"
|
||||
@ -24,11 +23,11 @@
|
||||
struct bay;
|
||||
|
||||
static struct thread *
|
||||
create_thread(struct proc *proc, const char *tracedir, const char *relpath)
|
||||
create_thread(struct proc *proc, struct stream *s)
|
||||
{
|
||||
int tid;
|
||||
if (thread_relpath_get_tid(relpath, &tid) != 0) {
|
||||
err("cannot get thread tid from %s", relpath);
|
||||
if ((tid = thread_stream_get_tid(s)) < 0) {
|
||||
err("cannot get thread tid from stream: %s", s->relpath);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
@ -45,21 +44,13 @@ create_thread(struct proc *proc, const char *tracedir, const char *relpath)
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (thread_init_begin(thread, relpath) != 0) {
|
||||
err("cannot init thread");
|
||||
if (thread_init_begin(thread, tid) != 0) {
|
||||
err("thread_init_begin failed: %s", s->relpath);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Build metadata path */
|
||||
char mpath[PATH_MAX];
|
||||
if (snprintf(mpath, PATH_MAX, "%s/%s/thread.%d.json",
|
||||
tracedir, proc->id, tid) >= PATH_MAX) {
|
||||
err("path too long");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (metadata_load_thread(mpath, thread) != 0) {
|
||||
err("cannot load metadata from %s", mpath);
|
||||
if (thread_load_metadata(thread, s) != 0) {
|
||||
err("thread_load_metadata failed: %s", s->relpath);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
@ -72,48 +63,39 @@ create_thread(struct proc *proc, const char *tracedir, const char *relpath)
|
||||
}
|
||||
|
||||
static struct proc *
|
||||
create_proc(struct loom *loom, const char *tracedir, const char *relpath)
|
||||
create_proc(struct loom *loom, struct stream *s)
|
||||
{
|
||||
int pid;
|
||||
if (proc_relpath_get_pid(relpath, &pid) != 0) {
|
||||
err("cannot get proc pid from %s", relpath);
|
||||
int pid = proc_stream_get_pid(s);
|
||||
if (pid < 0) {
|
||||
err("cannot get proc pid from stream: %s", s->relpath);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
struct proc *proc = loom_find_proc(loom, pid);
|
||||
|
||||
if (proc != NULL)
|
||||
return proc;
|
||||
|
||||
proc = malloc(sizeof(struct proc));
|
||||
|
||||
if (proc == NULL) {
|
||||
err("malloc failed:");
|
||||
return NULL;
|
||||
/* Create a new process */
|
||||
|
||||
proc = malloc(sizeof(struct proc));
|
||||
|
||||
if (proc == NULL) {
|
||||
err("malloc failed:");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (proc_init_begin(proc, pid) != 0) {
|
||||
err("proc_init_begin failed: %s", s->relpath);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (loom_add_proc(loom, proc) != 0) {
|
||||
err("loom_add_proc failed");
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
if (proc_init_begin(proc, relpath) != 0) {
|
||||
err("proc_init_begin failed");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Build metadata path */
|
||||
char mpath[PATH_MAX];
|
||||
|
||||
if (snprintf(mpath, PATH_MAX, "%s/%s/metadata.json",
|
||||
tracedir, proc->id) >= PATH_MAX) {
|
||||
err("path too long");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Load metadata too */
|
||||
if (metadata_load_proc(mpath, loom, proc) != 0) {
|
||||
err("cannot load metadata from %s", mpath);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (loom_add_proc(loom, proc) != 0) {
|
||||
err("loom_add_proc failed");
|
||||
/* The appid is populated from the metadata */
|
||||
if (proc_load_metadata(proc, s) != 0) {
|
||||
err("proc_load_metadata failed");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
@ -131,16 +113,11 @@ find_loom(struct system *sys, const char *id)
|
||||
}
|
||||
|
||||
static struct loom *
|
||||
create_loom(struct system *sys, const char *relpath)
|
||||
create_loom(struct system *sys, struct stream *s)
|
||||
{
|
||||
char name[PATH_MAX];
|
||||
if (snprintf(name, PATH_MAX, "%s", relpath) >= PATH_MAX) {
|
||||
err("path too long: %s", relpath);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (strtok(name, "/") == NULL) {
|
||||
err("cannot find first '/': %s", relpath);
|
||||
const char *name = loom_name(s);
|
||||
if (name == NULL) {
|
||||
err("loom_name failed");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
@ -163,6 +140,11 @@ create_loom(struct system *sys, const char *relpath)
|
||||
sys->nlooms++;
|
||||
}
|
||||
|
||||
if (loom_load_metadata(loom, s) != 0) {
|
||||
err("loom_load_metadata failed for stream: %s", s->relpath);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return loom;
|
||||
}
|
||||
|
||||
@ -220,13 +202,35 @@ report_libovni_version(struct system *sys)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
is_thread_stream(struct stream *s)
|
||||
{
|
||||
JSON_Object *meta = stream_metadata(s);
|
||||
if (meta == NULL) {
|
||||
err("no metadata for stream: %s", s->relpath);
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* All streams must have a ovni.part attribute */
|
||||
const char *part_type = json_object_dotget_string(meta, "ovni.part");
|
||||
|
||||
if (part_type == NULL) {
|
||||
err("cannot get attribute ovni.part for stream: %s",
|
||||
s->relpath);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (strcmp(part_type, "thread") == 0)
|
||||
return 1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
create_system(struct system *sys, struct trace *trace)
|
||||
{
|
||||
const char *dir = trace->tracedir;
|
||||
|
||||
/* Allocate the lpt map */
|
||||
sys->lpt = calloc(trace->nstreams, sizeof(struct lpt));
|
||||
sys->lpt = calloc((size_t) trace->nstreams, sizeof(struct lpt));
|
||||
if (sys->lpt == NULL) {
|
||||
err("calloc failed:");
|
||||
return -1;
|
||||
@ -234,25 +238,28 @@ create_system(struct system *sys, struct trace *trace)
|
||||
|
||||
size_t i = 0;
|
||||
for (struct stream *s = trace->streams; s ; s = s->next) {
|
||||
if (!loom_matches(s->relpath)) {
|
||||
int ok = is_thread_stream(s);
|
||||
if (ok < 0) {
|
||||
err("is_thread_stream failed");
|
||||
return -1;
|
||||
} else if (ok == 0) {
|
||||
warn("ignoring unknown stream %s", s->relpath);
|
||||
continue;
|
||||
}
|
||||
|
||||
struct loom *loom = create_loom(sys, s->relpath);
|
||||
struct loom *loom = create_loom(sys, s);
|
||||
if (loom == NULL) {
|
||||
err("create_loom failed");
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Loads metadata too */
|
||||
struct proc *proc = create_proc(loom, dir, s->relpath);
|
||||
struct proc *proc = create_proc(loom, s);
|
||||
if (proc == NULL) {
|
||||
err("create_proc failed");
|
||||
return -1;
|
||||
}
|
||||
|
||||
struct thread *thread = create_thread(proc, dir, s->relpath);
|
||||
struct thread *thread = create_thread(proc, s);
|
||||
if (thread == NULL) {
|
||||
err("create_thread failed");
|
||||
return -1;
|
||||
@ -267,14 +274,6 @@ create_system(struct system *sys, struct trace *trace)
|
||||
stream_data_set(s, lpt);
|
||||
}
|
||||
|
||||
/* Ensure all looms have at least one CPU */
|
||||
for (struct loom *l = sys->looms; l; l = l->next) {
|
||||
if (l->ncpus == 0) {
|
||||
err("loom %s has no physical CPUs", l->id);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -340,28 +339,28 @@ print_system(struct system *sys)
|
||||
{
|
||||
err("content of system: ");
|
||||
for (struct loom *l = sys->looms; l; l = l->next) {
|
||||
err("%s gindex=%d", l->id, l->gindex);
|
||||
err("+ %ld processes: ", l->nprocs);
|
||||
err("%s gindex=%"PRIi64, l->id, l->gindex);
|
||||
err("+ %zd processes: ", l->nprocs);
|
||||
for (struct proc *p = l->procs; p; p = p->hh.next) {
|
||||
err("| %s gindex=%d pid=%d",
|
||||
err("| %s gindex=%"PRIi64" pid=%d",
|
||||
p->id, p->gindex, p->pid);
|
||||
err("| + %ld threads: ", p->nthreads);
|
||||
err("| + %d threads: ", p->nthreads);
|
||||
for (struct thread *t = p->threads; t; t = t->hh.next) {
|
||||
err("| | %s tid=%d",
|
||||
t->id, t->tid);
|
||||
}
|
||||
}
|
||||
err("+ %ld phy cpus: ", l->ncpus);
|
||||
err("+ %zd phy cpus: ", l->ncpus);
|
||||
for (struct cpu *cpu = l->cpus; cpu; cpu = cpu->hh.next) {
|
||||
err("| %s gindex=%ld phyid=%d",
|
||||
err("| %s gindex=%"PRIi64" phyid=%d",
|
||||
cpu->name,
|
||||
cpu->gindex,
|
||||
cpu->phyid);
|
||||
}
|
||||
|
||||
err("+ 1 virtual cpu: ", l->ncpus);
|
||||
err("+ 1 virtual cpu: ");
|
||||
struct cpu *cpu = &l->vcpu;
|
||||
err("| %s gindex=%ld phyid=%d",
|
||||
err("| %s gindex=%"PRIi64" phyid=%d",
|
||||
cpu->name,
|
||||
cpu->gindex,
|
||||
cpu->phyid);
|
||||
@ -373,20 +372,20 @@ init_global_indices(struct system *sys)
|
||||
{
|
||||
size_t iloom = 0;
|
||||
for (struct loom *l = sys->looms; l; l = l->next)
|
||||
loom_set_gindex(l, iloom++);
|
||||
loom_set_gindex(l, (int64_t) iloom++);
|
||||
|
||||
sys->nprocs = 0;
|
||||
for (struct proc *p = sys->procs; p; p = p->gnext)
|
||||
proc_set_gindex(p, sys->nprocs++);
|
||||
proc_set_gindex(p, (int64_t) sys->nprocs++);
|
||||
|
||||
sys->nthreads = 0;
|
||||
for (struct thread *t = sys->threads; t; t = t->gnext)
|
||||
thread_set_gindex(t, sys->nthreads++);
|
||||
thread_set_gindex(t, (int64_t) sys->nthreads++);
|
||||
|
||||
sys->ncpus = 0;
|
||||
sys->nphycpus = 0;
|
||||
for (struct cpu *c = sys->cpus; c; c = c->next) {
|
||||
cpu_set_gindex(c, sys->ncpus++);
|
||||
cpu_set_gindex(c, (int64_t) sys->ncpus++);
|
||||
if (!c->is_virtual)
|
||||
sys->nphycpus++;
|
||||
}
|
||||
@ -424,7 +423,7 @@ init_end_system(struct system *sys)
|
||||
}
|
||||
}
|
||||
|
||||
info("loaded %ld looms, %ld processes, %ld threads and %ld cpus",
|
||||
info("loaded %zd looms, %zd processes, %zd threads and %zd cpus",
|
||||
sys->nlooms, sys->nprocs, sys->nthreads, sys->nphycpus);
|
||||
|
||||
return 0;
|
||||
@ -518,7 +517,7 @@ init_offsets(struct system *sys, struct trace *trace)
|
||||
/* If we have more than one hostname and no offset table has been found,
|
||||
* we won't be able to synchronize the clocks */
|
||||
if (n == 0 && sys->nlooms > 1) {
|
||||
warn("no clock offset file loaded with %ld looms",
|
||||
warn("no clock offset file loaded with %zd looms",
|
||||
sys->nlooms);
|
||||
}
|
||||
|
||||
@ -552,6 +551,12 @@ set_sort_criteria(struct system *sys)
|
||||
int some_have = 0;
|
||||
int all_have = 1;
|
||||
for (struct loom *l = sys->looms; l; l = l->next) {
|
||||
/* Set the rank_min for later sorting */
|
||||
if (loom_set_rank_min(l) != 0) {
|
||||
err("loom_set_rank_min failed");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (l->rank_enabled)
|
||||
some_have = 1;
|
||||
else
|
||||
@ -641,13 +646,13 @@ system_connect(struct system *sys, struct bay *bay, struct recorder *rec)
|
||||
{
|
||||
/* Create Paraver traces */
|
||||
struct pvt *pvt_cpu = NULL;
|
||||
if ((pvt_cpu = recorder_add_pvt(rec, "cpu", sys->ncpus)) == NULL) {
|
||||
if ((pvt_cpu = recorder_add_pvt(rec, "cpu", (long) sys->ncpus)) == NULL) {
|
||||
err("recorder_add_pvt failed");
|
||||
return -1;
|
||||
}
|
||||
|
||||
struct pvt *pvt_th = NULL;
|
||||
if ((pvt_th = recorder_add_pvt(rec, "thread", sys->nthreads)) == NULL) {
|
||||
if ((pvt_th = recorder_add_pvt(rec, "thread", (long) sys->nthreads)) == NULL) {
|
||||
err("recorder_add_pvt failed");
|
||||
return -1;
|
||||
}
|
||||
@ -667,7 +672,7 @@ system_connect(struct system *sys, struct bay *bay, struct recorder *rec)
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (prf_add(prf, th->gindex, name) != 0) {
|
||||
if (prf_add(prf, (long) th->gindex, name) != 0) {
|
||||
err("prf_add failed for thread '%s'", th->id);
|
||||
return -1;
|
||||
}
|
||||
@ -688,7 +693,7 @@ system_connect(struct system *sys, struct bay *bay, struct recorder *rec)
|
||||
}
|
||||
|
||||
struct prf *prf = pvt_get_prf(pvt_cpu);
|
||||
if (prf_add(prf, cpu->gindex, cpu->name) != 0) {
|
||||
if (prf_add(prf, (long) cpu->gindex, cpu->name) != 0) {
|
||||
err("prf_add failed for cpu '%s'", cpu->name);
|
||||
return -1;
|
||||
}
|
||||
|
@ -1,4 +1,4 @@
|
||||
/* Copyright (c) 2021-2023 Barcelona Supercomputing Center (BSC)
|
||||
/* Copyright (c) 2021-2024 Barcelona Supercomputing Center (BSC)
|
||||
* SPDX-License-Identifier: GPL-3.0-or-later */
|
||||
|
||||
#include "task.h"
|
||||
@ -258,13 +258,22 @@ task_type_create(struct task_info *info, uint32_t type_id, const char *label)
|
||||
return -1;
|
||||
}
|
||||
|
||||
type->gid = task_get_type_gid(label);
|
||||
int n = snprintf(type->label, MAX_PCF_LABEL, "%s", label);
|
||||
int n;
|
||||
if (label[0] == '\0') {
|
||||
/* Give a name if empty */
|
||||
n = snprintf(type->label, MAX_PCF_LABEL,
|
||||
"(unlabeled task type %u)", type_id);
|
||||
} else {
|
||||
n = snprintf(type->label, MAX_PCF_LABEL, "%s", label);
|
||||
}
|
||||
|
||||
if (n >= MAX_PCF_LABEL) {
|
||||
err("task type label too long: %s", label);
|
||||
err("task type %u label too long", type_id);
|
||||
return -1;
|
||||
}
|
||||
|
||||
type->gid = task_get_type_gid(type->label);
|
||||
|
||||
/* Add the new task type to the hash table */
|
||||
HASH_ADD_INT(info->types, id, type);
|
||||
|
||||
@ -279,7 +288,7 @@ task_create_pcf_types(struct pcf_type *pcftype, struct task_type *types)
|
||||
|
||||
/* Emit types for all task types */
|
||||
for (struct task_type *tt = types; tt != NULL; tt = tt->hh.next) {
|
||||
struct pcf_value *pcfvalue = pcf_find_value(pcftype, tt->gid);
|
||||
struct pcf_value *pcfvalue = pcf_find_value(pcftype, (int) tt->gid);
|
||||
if (pcfvalue != NULL) {
|
||||
/* Ensure the label is the same, so we know that
|
||||
* no collision occurred */
|
||||
@ -292,7 +301,7 @@ task_create_pcf_types(struct pcf_type *pcftype, struct task_type *types)
|
||||
}
|
||||
}
|
||||
|
||||
if (pcf_add_value(pcftype, tt->gid, tt->label) == NULL) {
|
||||
if (pcf_add_value(pcftype, (int) tt->gid, tt->label) == NULL) {
|
||||
err("pcf_add_value failed");
|
||||
return -1;
|
||||
}
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user