Skip to content

Commit 59ca6bc

Browse files
Jip J. Dekkerjemc
Jip J. Dekker
authored andcommitted
Add DTrace/SystemTap support to Pony (ponylang#1324)
In this contribution we have replaced the telemetry functionality of the compiler by scripts for both technologies. It shows the clean approach of DTrace/SystemTap. Support for Windows might be limited by this change. However since there was no support for the telemetry in the Windows build tools, we thought this would be the best approach. Currently implemented probes have a focus on replacing the events used by the telemetry functionality. We plan to add additional probes in the coming weeks, but we were hoping on possible feedback to change our approach if necessary. Contributors to this addition are: @mkdubik, @DarkLog1x, and me
1 parent 50faab2 commit 59ca6bc

File tree

15 files changed

+649
-127
lines changed

15 files changed

+649
-127
lines changed

.gitignore

+1
Original file line numberDiff line numberDiff line change
@@ -15,3 +15,4 @@ work/
1515
output/
1616
_ReSharper.*
1717
.vscode/
18+
src/common/dtrace_probes.h

CHANGELOG.md

+4
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,12 @@ All notable changes to the Pony compiler and standard library will be documented
88

99
### Added
1010

11+
- DTrace and SystemTap support - `use=dtrace`
12+
1113
### Changed
1214

15+
- Replaces `use=telemetry` by DTrace/SystemTap scripts
16+
1317
## [0.5.1] - 2016-10-15
1418

1519
### Fixed

Makefile

+24-4
Original file line numberDiff line numberDiff line change
@@ -124,9 +124,14 @@ ifdef use
124124
PONY_BUILD_DIR := $(PONY_BUILD_DIR)-pooltrack
125125
endif
126126

127-
ifneq (,$(filter $(use), telemetry))
128-
ALL_CFLAGS += -DUSE_TELEMETRY
129-
PONY_BUILD_DIR := $(PONY_BUILD_DIR)-telemetry
127+
ifneq (,$(filter $(use), dtrace))
128+
DTRACE ?= $(shell which dtrace)
129+
ifeq (, $(DTRACE))
130+
$(error No dtrace compatible user application static probe generation tool found)
131+
endif
132+
133+
ALL_CFLAGS += -DUSE_DYNAMIC_TRACE
134+
PONY_BUILD_DIR := $(PONY_BUILD_DIR)-dtrace
130135
endif
131136
endif
132137

@@ -371,6 +376,10 @@ ifeq ($(OSTYPE),linux)
371376
libponyrt.tests.links += pthread dl
372377
endif
373378

379+
ifneq (, $(DTRACE))
380+
$(shell $(DTRACE) -h -s $(PONY_SOURCE_DIR)/common/dtrace_probes.d -o $(PONY_SOURCE_DIR)/common/dtrace_probes.h)
381+
endif
382+
374383
# Overwrite the default linker for a target.
375384
ponyc.linker = $(CXX) #compile as C but link as CPP (llvm)
376385

@@ -523,7 +532,17 @@ $(foreach d,$($(1).depends),$(eval depends += $($(d))/$(d).$(LIB_EXT)))
523532
ifeq ($(1),libponyrt)
524533
$($(1))/libponyrt.$(LIB_EXT): $(depends) $(ofiles)
525534
@echo 'Linking libponyrt'
535+
ifneq (,$(DTRACE))
536+
ifeq ($(OSTYPE), linux)
537+
@echo 'Generating dtrace object file'
538+
$(SILENT)$(DTRACE) -G -s $(PONY_SOURCE_DIR)/common/dtrace_probes.d -o $(PONY_BUILD_DIR)/dtrace_probes.o
539+
$(SILENT)$(AR) $(AR_FLAGS) $$@ $(ofiles) $(PONY_BUILD_DIR)/dtrace_probes.o
540+
else
526541
$(SILENT)$(AR) $(AR_FLAGS) $$@ $(ofiles)
542+
endif
543+
else
544+
$(SILENT)$(AR) $(AR_FLAGS) $$@ $(ofiles)
545+
endif
527546
ifeq ($(runtime-bitcode),yes)
528547
$($(1))/libponyrt.bc: $(depends) $(bcfiles)
529548
@echo 'Generating bitcode for libponyrt'
@@ -685,6 +704,7 @@ stats:
685704

686705
clean:
687706
@rm -rf $(PONY_BUILD_DIR)
707+
@rm src/common/dtrace_probes.h
688708
-@rmdir build 2>/dev/null ||:
689709
@echo 'Repository cleaned ($(PONY_BUILD_DIR)).'
690710

@@ -702,7 +722,7 @@ help:
702722
@echo 'USE OPTIONS:'
703723
@echo ' valgrind'
704724
@echo ' pooltrack'
705-
@echo ' telemetry'
725+
@echo ' dtrace'
706726
@echo
707727
@echo 'TARGETS:'
708728
@echo ' libponyc Pony compiler library'

examples/dtrace/README.md

+131
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,131 @@
1+
# DTrace in PonyC
2+
3+
[DTrace](http://dtrace.org/guide/preface.html) provides a library for dynamic
4+
tracing of events. This document describes the implementation of DTrace within
5+
the Pony compiler. In particular, it focuses on how to use and extend the DTrace
6+
implementation within Pony.
7+
8+
DTrace is only available for MacOS and BSD. For Linux there is an alternative
9+
called [SystemTap](https://sourceware.org/systemtap/). SystemTap uses the same
10+
resources as DTrace, but uses different scripts. You will find more information
11+
on using SystemTap within the Pony compiler [here](../systemtap/README.md).
12+
13+
## Using DTrace scripts for Pony
14+
15+
You can find various example DTrace scripts in `example/dtrace/`. To run these
16+
scripts, you can use a command in the form of `[script] -c [binary]`. You need
17+
to use a PonyC compiler compiled with the DTrace flag to use DTrace. To compile
18+
a DTrace enabled compiler, you can use the following command: `make
19+
config=release use=dtrace`.
20+
21+
We can also execute a script using the dtrace command. This is necessary if the
22+
script does not contain a "Shebang". We can do it using the following command:
23+
`dtrace -s [script] -c [binary]`.
24+
25+
### MacOS 10.11+
26+
27+
MacOS El Capitan for the first time included "System Integrity Protection". This
28+
feature does not allow for all DTrace features to be used. This includes custom
29+
DTrace providers. To get around this problem, the following steps can be
30+
followed:
31+
32+
1. Boot your Mac into Recovery Mode.
33+
2. Open the Terminal from the Utilities menu.
34+
3. Then use the following commands:
35+
36+
```bash
37+
38+
csrutil clear # restore the default configuration first
39+
40+
csrutil enable --without dtrace # disable dtrace restrictions *only*
41+
42+
```
43+
44+
After completing these steps your Mac should be able to use DTrace after a
45+
restart. For more information visit the following article:
46+
[link](http://internals.exposed/blog/dtrace-vs-sip.html).
47+
48+
## Writing scripts for DTrace in Pony
49+
50+
The following paragraph will inform you on the Pony provider and its probes for
51+
DTrace. We refer to the [DTrace
52+
documentation](http://dtrace.org/guide/preface.html) for more information on the
53+
syntax and possibilities.
54+
55+
The Pony provider for DTrace consists of DTrace probes implemented in the
56+
Runtime. A binary compiled with DTrace enabled will allow the user access to the
57+
information of the probes. You can find a list of all probes and their arguments
58+
in [`src/common/dtrace_probes.d`](../../src/common/dtrace_probes.d). The
59+
following is a toy example DTrace script:
60+
61+
```
62+
63+
pony$target:::gc-start
64+
{
65+
@ = count();
66+
}
67+
68+
END
69+
{
70+
printa(@);
71+
}
72+
73+
```
74+
75+
This script increases a counter every time the "GC Start" probe is *fired* and
76+
prints it result at the end. Note the way in which we access the probe. It
77+
consists of two parts: the first part is the provider and the second part, after `:::`
78+
is the name of the probe. The provider is during runtime appended by the process
79+
ID. In this example we use the `$target` variable to find the process ID.
80+
Another option is to use `pony*`, but note that this could match many processes.
81+
The name of the probe are also different from the names in
82+
[`src/common/dtrace_probes.d`](../../src/common/dtrace_probes.d). In Dtrace
83+
scripts you have to replace the `__` in the names by `-`.
84+
85+
To make these scripts executable, like the ones in the examples, we use the
86+
following "Shebang": `#!/usr/bin/env dtrace -s`. Extra options can be added to
87+
improve its functionality.
88+
89+
## Extending the DTrace implementation in Pony
90+
91+
You can extend the DTrace implementation by adding more probes or extra
92+
information to existing probes. All probes are defined in
93+
[`src/common/dtrace_probes.d`](../../src/common/dtrace_probes.d). Usually their
94+
names of their module and the event that triggers them. To install Probes in C
95+
use the macros defined in `src/common/dtrace.h`. To fire a probe in the C code
96+
use the macro `DTRACEx`; where `x` is the number of arguments of the probe.
97+
There is also a macro `DTRACE_ENABLED`; its use allows for code to be only
98+
executed when a probe is enabled.
99+
100+
### Adding a probe
101+
102+
The first step to add a probe is to add its definition into
103+
[`src/common/dtrace_probes.d`](../../src/common/dtrace_probes.d). We have split
104+
the names of the probes into two parts: its category and the specific event. The
105+
name is split using `__`. We also add a comment to a probe explaining when it's
106+
fired and the information contained in its arguments.
107+
108+
After adding the probe, we use it in the C code using the macros. Note that the
109+
split in the name is only a single underscore in the C code. The name of the
110+
probe should also be capitalised. We can call the probe defined as `gc__start`
111+
using the statement `DTRACE1(GC_START, scheduler)`. In this statement
112+
`scheduler` is the data used as the first argument.
113+
114+
Then once the probe has been placed in all the appropriate places, we are ready
115+
to recompile. Make sure to use the DTrace flag while compiling. Recompiling will
116+
create the appropriate files using the system installation of DTrace.
117+
118+
### Extending a probe
119+
120+
We can extend a probe by adding an extra argument to the probe. Like adding a
121+
probe, the probe definition needs to be appended in
122+
[`src/common/dtrace_probes.d`](../../src/common/dtrace_probes.d). Note that this
123+
extra information needs to be available **everywhere** the probe is used.
124+
125+
Once you've added the argument, you need to change **all** instances where the
126+
probe is in use. Note that you have to both add the new argument and change the
127+
`DTRACE` macro. Then you can recompile the PonyC compiler to use your new
128+
arguments.
129+
130+
*Do not forget that if you change the order of the arguments for any of the
131+
existing nodes, you also need to change their usage in the example scripts.*

examples/dtrace/gc.d

+22
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
#!/usr/bin/env dtrace -s
2+
3+
#pragma D option quiet
4+
5+
pony$target:::gc-start
6+
{
7+
@count["GC Passes"] = count();
8+
self->start_gc = timestamp;
9+
}
10+
11+
pony$target:::gc-end
12+
{
13+
@quant["Time in GC (ns)"] = quantize(timestamp - self->start_gc);
14+
@times["Total time"] = sum(timestamp - self->start_gc);
15+
}
16+
17+
END
18+
{
19+
printa(@count);
20+
printa(@quant);
21+
printa(@times);
22+
}

examples/dtrace/telemetry.d

+103
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,103 @@
1+
#!/usr/bin/env dtrace -x aggsortkey -x aggsortkeypos=1 -s
2+
3+
#pragma D option quiet
4+
5+
inline unsigned int UINT32_MAX = -1;
6+
inline unsigned int ACTORMSG_BLOCK = (UINT32_MAX - 6);
7+
inline unsigned int ACTORMSG_UNBLOCK = (UINT32_MAX - 5);
8+
inline unsigned int ACTORMSG_ACQUIRE = (UINT32_MAX - 4);
9+
inline unsigned int ACTORMSG_RELEASE = (UINT32_MAX - 3);
10+
inline unsigned int ACTORMSG_CONF = (UINT32_MAX - 2);
11+
inline unsigned int ACTORMSG_ACK = (UINT32_MAX - 1);
12+
13+
pony$target:::actor-msg-send
14+
/ (unsigned int)arg1 == (unsigned int)ACTORMSG_BLOCK /
15+
{
16+
@counts[arg0, "Block Messages Sent"] = count();
17+
}
18+
19+
pony$target:::actor-msg-send
20+
/ (unsigned int)arg1 == (unsigned int)ACTORMSG_UNBLOCK /
21+
{
22+
@counts[arg0, "Unblock Messages Sent"] = count();
23+
}
24+
25+
pony$target:::actor-msg-send
26+
/ (unsigned int)arg1 == (unsigned int)ACTORMSG_ACQUIRE /
27+
{
28+
@counts[arg0, "Acquire Messages Sent"] = count();
29+
}
30+
31+
pony$target:::actor-msg-send
32+
/ (unsigned int)arg1 == (unsigned int)ACTORMSG_RELEASE /
33+
{
34+
@counts[arg0, "Release Messages Sent"] = count();
35+
}
36+
37+
pony$target:::actor-msg-send
38+
/ (unsigned int)arg1 == (unsigned int)ACTORMSG_CONF /
39+
{
40+
@counts[arg0, "Confirmation Messages Sent"] = count();
41+
}
42+
43+
pony$target:::actor-msg-send
44+
/ (unsigned int)arg1 == (unsigned int)ACTORMSG_ACK /
45+
{
46+
@counts[arg0, "Acknowledgement Messages Sent"] = count();
47+
}
48+
49+
pony$target:::actor-msg-send
50+
/ (unsigned int)arg1 < (unsigned int)ACTORMSG_BLOCK /
51+
{
52+
@counts[arg0, "Application Messages Sent"] = count();
53+
}
54+
55+
pony$target:::actor-alloc
56+
{
57+
@counts[arg0, "Actor Allocations"] = count();
58+
}
59+
60+
pony$target:::heap-alloc
61+
{
62+
@counts[arg0, "Heap Allocations"] = count();
63+
@sizes[arg0, "Heap Allocations Size"] = sum(arg1);
64+
}
65+
66+
pony$target:::gc-start
67+
{
68+
@counts[arg0, "GC Passes"] = count();
69+
self->start_gc = timestamp;
70+
}
71+
72+
pony$target:::gc-end
73+
{
74+
@times[arg0, "Time in GC"] = sum(timestamp - self->start_gc);
75+
}
76+
77+
pony$target:::gc-send-start
78+
{
79+
@counts[arg0, "Objects Sent"] = count();
80+
self->start_send = timestamp;
81+
}
82+
83+
pony$target:::gc-send-end
84+
{
85+
@times[arg0, "Time in Send Scan"] = sum(timestamp - self->start_send);
86+
}
87+
88+
pony$target:::gc-recv-start
89+
{
90+
@counts[arg0, "Objects Received"] = count();
91+
self->start_recv = timestamp;
92+
}
93+
94+
pony$target:::gc-recv-end
95+
{
96+
@times[arg0, "Time in Recv Scan"] = sum(timestamp - self->start_recv);
97+
}
98+
99+
END
100+
{
101+
printf("%?s %-40s %10s %10s %10s\n", "SCHEDULER", "EVENT", "COUNT", "TIME (ns)", "SIZE");
102+
printa("%?p %-40s %@10d %@10d %@10d\n", @counts, @times, @sizes);
103+
}

0 commit comments

Comments
 (0)