Skip to content

Commit

Permalink
Merge branch 'master' into stp
Browse files Browse the repository at this point in the history
  • Loading branch information
prasadtiru authored Apr 23, 2021
2 parents aad3496 + 52acfd1 commit 5ff6562
Show file tree
Hide file tree
Showing 6 changed files with 326 additions and 1 deletion.
41 changes: 41 additions & 0 deletions tools/viztrace/Manifest.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
# This file is machine-generated - editing it directly is not advised

[[ArgParse]]
deps = ["Logging", "TextWrap"]
git-tree-sha1 = "4a8f4df432fd8e8a96a142c53f9432b9022a92e6"
uuid = "c7e460c6-2fb9-53a9-8c5b-16f535851c63"
version = "1.1.1"

[[Dates]]
deps = ["Printf"]
uuid = "ade2ca70-3891-5945-98fb-dc099432e06a"

[[JSON]]
deps = ["Dates", "Mmap", "Parsers", "Unicode"]
git-tree-sha1 = "81690084b6198a2e1da36fcfda16eeca9f9f24e4"
uuid = "682c06a0-de6a-54ab-a142-c8b1cf79cde6"
version = "0.21.1"

[[Logging]]
uuid = "56ddb016-857b-54e1-b83d-db4d58db5568"

[[Mmap]]
uuid = "a63ad114-7e13-5084-954f-fe012c677804"

[[Parsers]]
deps = ["Dates"]
git-tree-sha1 = "50c9a9ed8c714945e01cd53a21007ed3865ed714"
uuid = "69de0a69-1ddd-5017-9359-2bf0b02dc9f0"
version = "1.0.15"

[[Printf]]
deps = ["Unicode"]
uuid = "de0858da-6303-5e67-8744-51eddeeeb8d7"

[[TextWrap]]
git-tree-sha1 = "9250ef9b01b66667380cf3275b3f7488d0e25faf"
uuid = "b718987f-49a8-5099-9789-dcd902bef87d"
version = "1.0.1"

[[Unicode]]
uuid = "4ec0a83e-493e-50e2-b9ac-8f72acf5a8f5"
3 changes: 3 additions & 0 deletions tools/viztrace/Project.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
[deps]
ArgParse = "c7e460c6-2fb9-53a9-8c5b-16f535851c63"
JSON = "682c06a0-de6a-54ab-a142-c8b1cf79cde6"
67 changes: 67 additions & 0 deletions tools/viztrace/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
# VizTrace
**Trace visualization tool for UnetStack JSON trace files**

UnetStack 3.3 introduced the event logging framework and JSON trace file format that contains detailed information for automated analysis of network traces. To illustrate the power of the event logging framework, we have built a simple viztrace tool to automatically draw sequence diagrams from the JSON trace files. The tool is written in Julia, and will require a working installation of [Julia](https://julialang.org/downloads/) on your machine to run.

To illustrate the power of the tool, let us simulate a simple [2-node network](https://unetstack.net/handbook/unet-handbook_getting_started.html) and make a range measurment from node A to B. On node A:

```
> range host('B')
999.99976
```

If you look in the `logs` folder in the simulator, you'll find a `trace.nam` file. We can analyze it using the `viztrace` tool:

```sh
$ julia --project viztrace.jl trace.json
Specify a trace:
1: 1617881734525 [B] AddressAllocReq ⟦ node → arp ⟧ (1 events)
2: 1617881734525 [A] AddressAllocReq ⟦ node → arp ⟧ (1 events)
3: 1617881734531 [A] AddressResolutionReq ⟦ websh → arp ⟧ (1 events)
4: 1617881734595 [A] RangeReq ⟦ websh → ranging ⟧ (23 events)
```

So the tool tells us that there are 4 event traces in the `trace.json` file. The first 2 traces are related to address allocations on nodes B and A. The third trace is an address resolution for node B, when we called `host('B')`. The final trace is the actual ranging event, consisting of 23 individual sub-events. Let's explore that in more detail:

```sh
$ julia --project viztrace.jl -t 4 trace.json > event4.mmd
```

This generates a [mermaid](https://mermaid-js.github.io/) sequence diagram for all the events in trace 4:

```
sequenceDiagram
participant websh_A as websh/A
participant ranging_A as ranging/A
participant mac_A as mac/A
participant phy_A as phy/A
participant phy_B as phy/B
participant ranging_B as ranging/B
websh_A->>ranging_A: RangeReq
ranging_A-->>websh_A: AGREE
ranging_A->>mac_A: ReservationReq
mac_A->>ranging_A: ReservationRsp
mac_A->>ranging_A: ReservationStatusNtf
ranging_A->>phy_A: ClearReq
phy_A-->>ranging_A: AGREE
ranging_A->>phy_A: TxFrameReq
phy_A-->>ranging_A: AGREE
phy_A->>ranging_A: TxFrameNtf
phy_A->>phy_B: HalfDuplexModem$TX
phy_B->>ranging_B: RxFrameNtf
ranging_B->>phy_B: ClearReq
phy_B-->>ranging_B: AGREE
ranging_B->>phy_B: TxFrameReq
phy_B-->>ranging_B: AGREE
phy_B->>ranging_B: TxFrameNtf
phy_B->>phy_A: HalfDuplexModem$TX
phy_A->>ranging_A: RxFrameNtf
ranging_A->>websh_A: RangeNtf
ranging_A->>mac_A: ReservationCancelReq
mac_A-->>ranging_A: AGREE
mac_A->>ranging_A: ReservationStatusNtf
```

We can easily convert this to a nice sequence diagram using the [mermaid command-line interface](https://github.com/mermaid-js/mermaid-cli) or the [mermaid online live editor](https://mermaid-js.github.io/mermaid-live-editor):

![](https://mermaid.ink/img/eyJjb2RlIjoic2VxdWVuY2VEaWFncmFtXG4gIHBhcnRpY2lwYW50IHdlYnNoX0EgYXMgd2Vic2gvQVxuICBwYXJ0aWNpcGFudCByYW5naW5nX0EgYXMgcmFuZ2luZy9BXG4gIHBhcnRpY2lwYW50IG1hY19BIGFzIG1hYy9BXG4gIHBhcnRpY2lwYW50IHBoeV9BIGFzIHBoeS9BXG4gIHBhcnRpY2lwYW50IHBoeV9CIGFzIHBoeS9CXG4gIHBhcnRpY2lwYW50IHJhbmdpbmdfQiBhcyByYW5naW5nL0JcbiAgd2Vic2hfQS0-PnJhbmdpbmdfQTogUmFuZ2VSZXFcbiAgcmFuZ2luZ19BLS0-PndlYnNoX0E6IEFHUkVFXG4gIHJhbmdpbmdfQS0-Pm1hY19BOiBSZXNlcnZhdGlvblJlcVxuICBtYWNfQS0-PnJhbmdpbmdfQTogUmVzZXJ2YXRpb25Sc3BcbiAgbWFjX0EtPj5yYW5naW5nX0E6IFJlc2VydmF0aW9uU3RhdHVzTnRmXG4gIHJhbmdpbmdfQS0-PnBoeV9BOiBDbGVhclJlcVxuICBwaHlfQS0tPj5yYW5naW5nX0E6IEFHUkVFXG4gIHJhbmdpbmdfQS0-PnBoeV9BOiBUeEZyYW1lUmVxXG4gIHBoeV9BLS0-PnJhbmdpbmdfQTogQUdSRUVcbiAgcGh5X0EtPj5yYW5naW5nX0E6IFR4RnJhbWVOdGZcbiAgcGh5X0EtPj5waHlfQjogSGFsZkR1cGxleE1vZGVtJFRYXG4gIHBoeV9CLT4-cmFuZ2luZ19COiBSeEZyYW1lTnRmXG4gIHJhbmdpbmdfQi0-PnBoeV9COiBDbGVhclJlcVxuICBwaHlfQi0tPj5yYW5naW5nX0I6IEFHUkVFXG4gIHJhbmdpbmdfQi0-PnBoeV9COiBUeEZyYW1lUmVxXG4gIHBoeV9CLS0-PnJhbmdpbmdfQjogQUdSRUVcbiAgcGh5X0ItPj5yYW5naW5nX0I6IFR4RnJhbWVOdGZcbiAgcGh5X0ItPj5waHlfQTogSGFsZkR1cGxleE1vZGVtJFRYXG4gIHBoeV9BLT4-cmFuZ2luZ19BOiBSeEZyYW1lTnRmXG4gIHJhbmdpbmdfQS0-PndlYnNoX0E6IFJhbmdlTnRmXG4gIHJhbmdpbmdfQS0-Pm1hY19BOiBSZXNlcnZhdGlvbkNhbmNlbFJlcVxuICBtYWNfQS0tPj5yYW5naW5nX0E6IEFHUkVFXG4gIG1hY19BLT4-cmFuZ2luZ19BOiBSZXNlcnZhdGlvblN0YXR1c050ZlxuIiwibWVybWFpZCI6e30sInVwZGF0ZUVkaXRvciI6ZmFsc2V9)
213 changes: 213 additions & 0 deletions tools/viztrace/viztrace.jl
Original file line number Diff line number Diff line change
@@ -0,0 +1,213 @@
import Pkg
Pkg.instantiate()

using ArgParse
import JSON

### analysis functions

function groups(data; init=Tuple{String,Any}[])
if "events" keys(data)
events = data["events"]
n = length(init)
for e events
groups(e; init)
end
length(init) == n && push!(init, (data["group"], events))
end
init
end

function findtrace(cache, ev)
if "threadID" keys(ev)
id = ev["threadID"]
id keys(cache) && return cache[id]
end
if "stimulus" keys(ev)
id = ev["stimulus"]["messageID"]
id keys(cache) && return cache[id]
end
0
end

function addtocache!(cache, ev, trace)
if "threadID" keys(ev)
id = ev["threadID"]
cache[id] = trace
end
if "stimulus" keys(ev)
id = ev["stimulus"]["messageID"]
cache[id] = trace
end
if "response" keys(ev)
id = ev["response"]["messageID"]
cache[id] = trace
end
end

function splitcomponent(s)
m = match(r"^(?<name>.*)::(?<clazz>[^/]*)/?(?<node>.*)$", s)
m === nothing && return "", "", ""
m[:name], m[:clazz], m[:node]
end

function prettymessage(msg)
clazz = msg["clazz"]
p = findlast('.', clazz)
p === nothing || (clazz = clazz[p+1:end])
sender = "sender" keys(msg) ? msg["sender"] : ""
recipient = "recipient" keys(msg) ? msg["recipient"] : ""
"$clazz$sender$recipient"
end

function analyze(events)
traces = Tuple{Int64,String,Vector{Any}}[]
cache = Dict{String,Int64}()
for ev events
trace = findtrace(cache, ev)
if trace == 0
cname, cclazz, cnode = splitcomponent(ev["component"])
cnode != "" && (cnode = "[$cnode] ")
desc = cnode * cname
if "stimulus" keys(ev)
desc = cnode * prettymessage(ev["stimulus"])
elseif "response" keys(ev)
desc = cnode * prettymessage(ev["response"])
end
push!(traces, (ev["time"], desc, [ev]))
trace = length(traces)
else
push!(traces[trace][3], ev)
end
addtocache!(cache, ev, trace)
end
traces
end

function capture!(actors, msgs, origin, node, agent, t, msg, stimulus)
"recipient" keys(msg) && return
recipient = msg["recipient"]
if startswith(recipient, '#')
stimulus || return
recipient = agent
end
onode = node
okey = msg["messageID"] * "" * recipient
if okey keys(origin)
n = origin[okey]
onode == n && return
onode = n
end
stimulus && "sender" keys(msg) && return
sender = "sender" keys(msg) ? msg["sender"] : agent
if node != ""
sender = sender * "/" * onode
recipient = recipient * "/" * node
end
clazz = msg["clazz"]
if clazz == "org.arl.fjage.Message"
clazz = msg["performative"]
else
p = findlast('.', clazz)
p === nothing || (clazz = clazz[p+1:end])
end
origin[okey] = node
sender == recipient && return
push!(actors, (onode, sender))
push!(actors, (node, recipient))
push!(msgs, (t, clazz, sender, recipient))
end

function sequence(events)
actors = Tuple{String,String}[]
msgs = Tuple{Int64,String,String,String}[]
origin = Dict{String,String}()
for ev events
cname, cclazz, cnode = splitcomponent(ev["component"])
"stimulus" keys(ev) && capture!(actors, msgs, origin, cnode, cname, ev["time"], ev["stimulus"], true)
"response" keys(ev) && capture!(actors, msgs, origin, cnode, cname, ev["time"], ev["response"], false)
end
unique!(sort!(actors; by = x -> x[1]))
actors, msgs
end

function mermaid(actors, msgs)
println("sequenceDiagram")
for a actors
id = replace(a[2], '/' => '_')
println(" participant $id as $(a[2])")
end
for m msgs
id1 = replace(m[3], '/' => '_')
id2 = replace(m[4], '/' => '_')
print(" $id1")
m[2] == "AGREE" && print('-')
println("->>$id2: $(m[2])")
end
end

### main

aps = ArgParseSettings()
@add_arg_table! aps begin
"--group", "-g"
help = "event group number"
arg_type = Int
default = 0
"--trace", "-t"
help = "trace number"
arg_type = Int
default = 0
"filename"
help = "JSON trace file"
default = "trace.json"
end
args = parse_args(ARGS, aps)

if !isfile(args["filename"])
println(args["filename"], " not found")
exit(1)
end

grps = groups(JSON.parsefile(args["filename"]))

if length(grps) < 1
println("no traces found")
exit(1)
end

g = args["group"]
length(grps) == 1 && g == 0 && (g = 1)

if g == 0
println("Specify an event group:")
for (i, g1) enumerate(grps)
println(i, ": ", g1[1], " (", length(g1[2]), " events)")
end
exit(1)
end

if g < 1 || length(grps) < g
println("invalid event group number")
exit(1)
end

traces = analyze(grps[g][2])

t = args["trace"]
length(traces) == 1 && t == 0 && (t = 1)

if t == 0
println("Specify a trace:")
for (i, t1) enumerate(traces)
println(i, ": ", t1[1], " ", t1[2], " (", length(t1[3]), " events)")
end
exit(1)
end

if t < 1 || length(traces) < t
println("invalid trace number")
exit(1)
end

mermaid(sequence(traces[t][3])...)
2 changes: 1 addition & 1 deletion unetsocket/c/unet.c
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
#define _BSD_SOURCE
#define _DEFAULT_SOURCE
#include <stdlib.h>
#include <errno.h>
#include "pthreadwindows.h"
Expand Down
1 change: 1 addition & 0 deletions unetsocket/python/unetpy/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -391,6 +391,7 @@ def receive(self):
def cancel(self):
"""Cancels an ongoing blocking receive()."""
if self.waiting:
self.gw.cancel = True
self.gw.cv.acquire()
self.gw.cv.notify()
self.gw.cv.release()
Expand Down

0 comments on commit 5ff6562

Please sign in to comment.