Compare commits

...

10 Commits

Author SHA1 Message Date
148aaa71a1 Make all atomic operations explicit
Some checks failed
CI / build:rt (pull_request) Failing after 23s
CI / build:debug (pull_request) Successful in 23s
CI / build:nompi (pull_request) Successful in 26s
CI / build:asan (pull_request) Failing after 31s
CI / build:local (pull_request) Successful in 1m18s
CI / build:compilers (pull_request) Successful in 38s
2024-10-15 12:33:52 +02:00
cdc5b9b866 Add trace diagram and organize concepts 2024-10-09 11:51:39 +02:00
5e502b67d2 Remove trailing slashes from tracedir 2024-09-17 14:30:10 +02:00
6aba89a8a8 Rename trace-model.md to just trace.md 2024-09-17 13:31:46 +02:00
9e6f691325 Add examples in some trace concepts 2024-09-17 13:30:21 +02:00
7ce892a9ff Add example program for mark API 2024-09-17 12:59:30 +02:00
4c58f4619b Document environment variables 2024-09-17 08:47:11 +02:00
8f4aa59148 Fix unit-stream test 2024-09-17 08:27:51 +02:00
d115ecad64 Remove tracing.md 2024-09-17 08:05:27 +02:00
73ce6ed035 Update trace specification to version 3 2024-09-17 08:05:15 +02:00
16 changed files with 877 additions and 286 deletions

View File

@ -13,9 +13,8 @@ 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.
<!-- FIXME: Add a index for runtime -->
The instrumentation process is split in two stages:
[runtime](user/runtime/tracing.md)
[runtime](user/runtime/index.md)
tracing and [emulation](user/emulation/index.md).
During runtime, very short binary events are stored on disk which

View File

@ -4,7 +4,7 @@ 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:
![lalala](part-model.svg "foo bar")
![Part model](part-model.svg)
Notice how a loom can restrict the CPUs of the node to its child processes.

View File

@ -1,72 +0,0 @@
# Trace model
An event model is composed by a group of runtime events
## Trace
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.
All the information is always stored inside the same directory, by default
`ovni/`, which is known as the trace directory.
## 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).
## 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.
## MCV
The MCV acronym is short of Model-Class-Value, which is a three character (byte)
identification for events.
## 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.
## Event model
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`.
## Payload
Events may have associated additional information which is stored in the stream.
## 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.
## Stream metadata
The stream metadata is a JSON file named `stream.json` which holds information
about the stream.
## 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).

123
doc/user/concepts/trace.md Normal file
View 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
View 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

56
doc/user/runtime/env.md Normal file
View 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
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 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.

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.6 KiB

View File

@ -15,14 +15,12 @@ To initialize libovni follow these steps in all threads:
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 when
a new process begins the execution. It can only be called **once per
process** and it must be called before the thread is initialized.
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()` when a new thread begins the
execution (including the main process thread after the process is
initialized). Multiple attempts to initialize the thread are ignored with a
warning.
3. **Init the thread**. Call `ovni_thread_init()` to initialize the thread.
Multiple attempts to initialize the same thread are ignored with a warning.
The `ovni_proc_init()` arguments are as follows:
@ -32,8 +30,8 @@ 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` defines the
[loom](../concepts/part-model.md#loom) name and assignes the process to that
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 compose of the host name, a dot and a suffix. The PID is the
one obtained by `getpid(2)`.
@ -59,8 +57,8 @@ the thread stream.
## 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 in the stream with
the appropriate payload:
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)

View File

@ -1,4 +1,4 @@
# Mark API
# 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
@ -80,6 +80,68 @@ 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

View File

@ -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:
![Stream](fig/stream.svg)
@ -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:
![Normal event with payload content](fig/event-normal-payload.svg)
## 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:
![Jumbo event](fig/event-jumbo.svg)
## 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.

View File

@ -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.md) 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
```

View File

@ -26,10 +26,10 @@ nav:
- user/installation.md
- 'Concepts':
- user/concepts/part-model.md
- user/concepts/trace-model.md
- user/concepts/trace.md
- 'Runtime':
- user/runtime/index.md
- user/runtime/tracing.md
- user/runtime/env.md
- user/runtime/mark.md
- user/runtime/distributed.md
- user/runtime/kernel.md

View File

@ -105,6 +105,10 @@ trace_load(struct trace *trace, const char *tracedir)
return -1;
}
/* Remove trailing slashes from tracedir */
path_remove_trailing(trace->tracedir);
tracedir = trace->tracedir;
/* Try to open the directory to catch permission errors */
DIR *dir = opendir(tracedir);
if (dir == NULL) {

View File

@ -180,7 +180,7 @@ ovni_add_cpu(int index, int phyid)
if (phyid < 0)
die("cannot use negative CPU id %d", phyid);
if (rproc.st != ST_READY)
if (atomic_load(&rproc.st) != ST_READY)
die("process not ready");
if (!rthread.ready)
@ -199,7 +199,7 @@ ovni_add_cpu(int index, int phyid)
void
ovni_proc_set_rank(int rank, int nranks)
{
if (rproc.st != ST_READY)
if (atomic_load(&rproc.st) != ST_READY)
die("process not ready");
if (!rthread.ready)
@ -273,7 +273,7 @@ ovni_proc_init(int app, const char *loom, int pid)
create_proc_dir(loom, pid);
rproc.st = ST_READY;
atomic_store(&rproc.st, ST_READY);
}
static int
@ -535,7 +535,7 @@ ovni_thread_init(pid_t tid)
if (tid == 0)
die("cannot use tid=%d", tid);
if (rproc.st != ST_READY)
if (atomic_load(&rproc.st) != ST_READY)
die("process not ready");
memset(&rthread, 0, sizeof(rthread));
@ -770,7 +770,7 @@ ovni_flush(void)
if (!rthread.ready)
die("thread is not initialized");
if (rproc.st != ST_READY)
if (atomic_load(&rproc.st) != ST_READY)
die("process not ready");
ovni_ev_set_clock(&pre, ovni_clock_now());

View File

@ -15,7 +15,7 @@ unit_test(cpu.c)
unit_test(loom.c)
unit_test(mux.c)
unit_test(prv.c)
#unit_test(stream.c) #FIXME
unit_test(stream.c)
unit_test(task.c)
unit_test(value.c)
unit_test(version.c)

View File

@ -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 <stdio.h>
@ -11,10 +11,27 @@
#include "ovni.h"
#include "unittest.h"
static void
write_dummy_json(const char *path)
{
const char *json = "{ \"version\" : 3 }";
FILE *f = fopen(path, "w");
if (f == NULL)
die("fopen json failed:");
if (fwrite(json, strlen(json), 1, f) != 1)
die("fwrite json failed:");
fclose(f);
}
static void
test_ok(void)
{
const char *fname = "stream-ok.obs";
OK(mkdir("ok", 0755));
const char *fname = "ok/stream.obs";
FILE *f = fopen(fname, "w");
if (f == NULL)
@ -30,8 +47,10 @@ test_ok(void)
fclose(f);
write_dummy_json("ok/stream.json");
struct stream stream;
OK(stream_load(&stream, ".", fname));
OK(stream_load(&stream, ".", "ok"));
if (stream.active)
die("stream is active");
@ -42,7 +61,9 @@ test_ok(void)
static void
test_bad(void)
{
const char *fname = "stream-bad.obs";
OK(mkdir("bad", 0755));
const char *fname = "bad/stream.obs";
FILE *f = fopen(fname, "w");
if (f == NULL)
@ -58,8 +79,10 @@ test_bad(void)
fclose(f);
write_dummy_json("bad/stream.json");
struct stream stream;
ERR(stream_load(&stream, ".", fname));
ERR(stream_load(&stream, ".", "bad"));
err("OK");
}