├── .gitignore ├── LICENCE ├── Makefile ├── README.md ├── test.pl ├── test_mem_usage.c ├── test_usdt.c ├── usdt.c ├── usdt.h ├── usdt_dof.c ├── usdt_dof_file.c ├── usdt_dof_sections.c ├── usdt_internal.h ├── usdt_probe.c ├── usdt_tracepoints_i386.s └── usdt_tracepoints_x86_64.s /.gitignore: -------------------------------------------------------------------------------- 1 | *.o 2 | *.a 3 | *.gch 4 | *~ 5 | *# 6 | .#* 7 | test_usdt 8 | test_usdt32 9 | test_usdt64 10 | test_mem_usage 11 | -------------------------------------------------------------------------------- /LICENCE: -------------------------------------------------------------------------------- 1 | Copyright 2012 Chris Andrews. All rights reserved. 2 | 3 | Redistribution and use in source and binary forms, with or without modification, are 4 | permitted provided that the following conditions are met: 5 | 6 | 1. Redistributions of source code must retain the above copyright notice, this list of 7 | conditions and the following disclaimer. 8 | 9 | 2. Redistributions in binary form must reproduce the above copyright notice, this list 10 | of conditions and the following disclaimer in the documentation and/or other materials 11 | provided with the distribution. 12 | 13 | THIS SOFTWARE IS PROVIDED BY CHRIS ANDREWS ``AS IS'' AND ANY EXPRESS OR IMPLIED 14 | WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND 15 | FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL CHRIS ANDREWS OR 16 | CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 17 | CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 18 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON 19 | ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 20 | NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF 21 | ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 22 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | CC = gcc 2 | CFLAGS = -O2 -Wall 3 | 4 | # MAC_BUILD - set this to "universal" to build a 2-way fat library 5 | MAC_BUILD = universal 6 | 7 | # if ARCH set, disable universal build on the mac 8 | ifdef ARCH 9 | MAC_BUILD = 10 | else 11 | ARCH = $(shell uname -m) 12 | endif 13 | 14 | UNAME = $(shell uname -s) 15 | 16 | ifeq ($(UNAME), Linux) 17 | RANLIB=ranlib 18 | CFLAGS+=-D_GNU_SOURCE -fPIC 19 | endif 20 | 21 | ifeq ($(UNAME), SunOS) 22 | RANLIB=/bin/true 23 | PATH +=:/usr/perl5/5.10.0/bin:/usr/perl5/5.12/bin 24 | CFLAGS += -fPIC 25 | ifeq ($(ARCH), i86pc) 26 | ARCH = $(shell isainfo -k) 27 | ifeq ($(ARCH), amd64) 28 | ARCH = x86_64 29 | else 30 | ARCH = i386 31 | endif 32 | endif 33 | ifeq ($(ARCH), x86_64) 34 | CFLAGS += -m64 35 | else 36 | CFLAGS += -m32 37 | endif 38 | endif 39 | 40 | ifeq ($(UNAME), FreeBSD) 41 | RANLIB=ranlib 42 | CFLAGS += -Wno-error=unknown-pragmas -I/usr/src/sys/cddl/compat/opensolaris -I/usr/src/sys/cddl/contrib/opensolaris/uts/common 43 | CFLAGS += -fPIC 44 | ifeq ($(ARCH), i386) 45 | CFLAGS += -m32 46 | endif 47 | ifeq ($(ARCH), amd64) 48 | ARCH = x86_64 49 | endif 50 | endif 51 | 52 | ifeq ($(UNAME), Darwin) 53 | RANLIB=ranlib 54 | ifeq ($(MAC_BUILD), universal) 55 | CFLAGS += -arch i386 -arch x86_64 56 | else 57 | CFLAGS += -arch $(ARCH) 58 | endif 59 | endif 60 | 61 | # main library build 62 | objects = usdt.o usdt_dof_file.o usdt_tracepoints.o usdt_probe.o usdt_dof.o usdt_dof_sections.o 63 | headers = usdt.h usdt_internal.h 64 | 65 | .c.o: $(headers) 66 | 67 | all: libusdt.a 68 | 69 | libusdt.a: $(objects) $(headers) 70 | rm -f libusdt.a 71 | $(AR) cru libusdt.a $(objects) 72 | $(RANLIB) libusdt.a 73 | 74 | # Tracepoints build. 75 | # 76 | # If on Darwin and building a universal library, manually assemble a 77 | # two-way fat object file from both the 32 and 64 bit tracepoint asm 78 | # files. 79 | # 80 | # Otherwise, just choose the appropriate asm file based on the build 81 | # architecture. 82 | 83 | ifeq ($(UNAME), Darwin) 84 | ifeq ($(MAC_BUILD), universal) 85 | 86 | usdt_tracepoints_i386.o: usdt_tracepoints_i386.s 87 | $(CC) -arch i386 -o usdt_tracepoints_i386.o -c usdt_tracepoints_i386.s 88 | 89 | usdt_tracepoints_x86_64.o: usdt_tracepoints_x86_64.s 90 | $(CC) -arch x86_64 -o usdt_tracepoints_x86_64.o -c usdt_tracepoints_x86_64.s 91 | 92 | usdt_tracepoints.o: usdt_tracepoints_i386.o usdt_tracepoints_x86_64.o 93 | lipo -create -output usdt_tracepoints.o usdt_tracepoints_i386.o \ 94 | usdt_tracepoints_x86_64.o 95 | 96 | else # Darwin, not universal 97 | usdt_tracepoints.o: usdt_tracepoints_$(ARCH).s 98 | $(CC) -arch $(ARCH) -o usdt_tracepoints.o -c usdt_tracepoints_$(ARCH).s 99 | endif 100 | 101 | else # not Darwin; FreeBSD and Illumos 102 | 103 | ifeq ($(ARCH), x86_64) 104 | usdt_tracepoints.o: usdt_tracepoints_x86_64.s 105 | $(CC) $(CFLAGS) -o usdt_tracepoints.o -c usdt_tracepoints_x86_64.s 106 | endif 107 | ifeq ($(ARCH), i386) 108 | usdt_tracepoints.o: usdt_tracepoints_i386.s 109 | $(CC) $(CFLAGS) -o usdt_tracepoints.o -c usdt_tracepoints_i386.s 110 | endif 111 | 112 | endif 113 | 114 | clean: 115 | rm -f *.gch 116 | rm -f *.o 117 | rm -f libusdt.a 118 | rm -f test_usdt 119 | rm -f test_usdt32 120 | rm -f test_usdt64 121 | rm -f test_mem_usage 122 | 123 | .PHONY: clean test 124 | 125 | # testing 126 | 127 | test_mem_usage: libusdt.a test_mem_usage.o 128 | $(CC) $(CFLAGS) -o test_mem_usage test_mem_usage.o libusdt.a 129 | 130 | ifeq ($(UNAME), Darwin) 131 | ifeq ($(MAC_BUILD), universal) 132 | test_usdt64: libusdt.a test_usdt.o 133 | $(CC) -arch x86_64 -o test_usdt64 test_usdt.o libusdt.a 134 | test_usdt32: libusdt.a test_usdt.o 135 | $(CC) -arch i386 -o test_usdt32 test_usdt.o libusdt.a 136 | else 137 | test_usdt: libusdt.a test_usdt.o 138 | $(CC) $(CFLAGS) -o test_usdt test_usdt.o libusdt.a 139 | endif 140 | else 141 | test_usdt: libusdt.a test_usdt.o 142 | $(CC) $(CFLAGS) -o test_usdt test_usdt.o libusdt.a 143 | endif 144 | 145 | ifeq ($(UNAME), Darwin) 146 | ifeq ($(MAC_BUILD), universal) 147 | test: test_usdt32 test_usdt64 148 | sudo prove test.pl :: 64 149 | sudo prove test.pl :: 32 150 | else 151 | test: test_usdt 152 | sudo prove test.pl 153 | endif 154 | else 155 | test: test_usdt 156 | sudo prove test.pl 157 | endif 158 | 159 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | libusdt 2 | ======= 3 | 4 | This is "libusdt", a library for creating DTrace USDT providers. 5 | 6 | The idea here is to allow the specification of a DTrace provider 7 | dynamically in code and then create the provider at runtime. This 8 | allows providers to be specified in dynamic languages, given suitable 9 | bindings. 10 | 11 | The general approach is to create two stub functions for each probe, 12 | one for the is-enabled check and one for the probe itself. These 13 | contain the appropriate instruction sequences to appear to DTrace as 14 | compiled-in tracepoints. A minimal DOF document is built describing 15 | the provider and indicating these stub functions as the tracepoints, 16 | then submitted to the kernel, creating the provider. The API then 17 | exposes the stubs, through which the probes may be fired. 18 | 19 | Status 20 | ------ 21 | 22 | The implementation here works as shown in test_usdt.c on Mac OS X, 23 | i386 and x86_64, on Solaris-like systems, i386 and x86_64 and on 24 | FreeBSD and Oracle Linux, x86_64 only. 25 | 26 | Is-enabled probes are supported and exposed in the API. 27 | 28 | There is a "test" target which runs a number of tests of the library, 29 | for which perl is required. 30 | 31 | OS X builds are Universal by default, and on Solaris, the ARCH 32 | variable may be set to either i386 or x86_64 to force a particular 33 | build. 34 | 35 | FreeBSD builds suffer from broken argument handling; this is a known 36 | issue with the current state of DTrace generally on FreeBSD: only the 37 | first five arguments work reliably. See: 38 | 39 | http://wiki.freebsd.org/DTraceTODO 40 | 41 | See Also 42 | -------- 43 | 44 | There are various language bindings available: 45 | 46 | Lua: 47 | 48 | https://github.com/chrisa/lua-usdt 49 | 50 | Ruby (by Kevin Chan): 51 | 52 | https://github.com/kevinykchan/ruby-usdt 53 | 54 | Node.JS: 55 | 56 | https://github.com/chrisa/node-dtrace-provider 57 | 58 | Perl: 59 | 60 | https://github.com/chrisa/perl-Devel-DTrace-Provider 61 | 62 | To Do 63 | ----- 64 | 65 | Platform support: 66 | 67 | * add support for FreeBSD 9.0 i386 68 | * add support for Mac OS X PowerPC 69 | * add support for Solaris SPARC 70 | 71 | Features: 72 | 73 | * add a "low level" API, allowing alternative provision of 74 | tracepoints for closer integration with language VMs. 75 | 76 | * support structured types, with close integration with the host 77 | DTrace system. 78 | -------------------------------------------------------------------------------- /test.pl: -------------------------------------------------------------------------------- 1 | #!/usr/bin/perl 2 | use strict; 3 | use warnings; 4 | use IPC::Open3; 5 | use Symbol 'gensym'; 6 | use IO::Handle; 7 | use Test::More qw/ no_plan /; 8 | 9 | my $USDT_ARG_MAX = 32; 10 | if ($^O eq 'freebsd') { 11 | # FreeBSD currently only supports 5 arguments to USDT probes 12 | $USDT_ARG_MAX = 5; 13 | } 14 | 15 | my $arch; 16 | if (scalar @ARGV == 1) { 17 | $arch = $ARGV[0]; 18 | } 19 | 20 | my $user_t = ($^O eq 'darwin') ? 'user_addr_t' : 'uintptr_t'; 21 | 22 | run_tests('c', 'A'); 23 | run_tests('i', 1); 24 | 25 | sub run_tests { 26 | my ($type, $start_arg) = @_; 27 | 28 | for my $i (0..$USDT_ARG_MAX) { 29 | my ($t_status, $d_status, $output) = run_dtrace('type'.$type, $i.'arg', split(//, $type x $i)); 30 | is($t_status, 0, 'test exit status is 0'); 31 | is($d_status, 0, 'dtrace exit status is 0'); 32 | like($output, qr/type[ic]:\d+arg/, 'function and name match'); 33 | 34 | my $arg = $start_arg; 35 | for my $j (0..$i - 1) { 36 | like($output, qr/arg$j:'\Q$arg\E'/, "type '$type' arg $j is $arg"); 37 | if ($type eq 'i') { 38 | $arg++; 39 | } 40 | else { 41 | $arg = chr(ord($arg) + 1); 42 | } 43 | } 44 | } 45 | } 46 | 47 | # -------------------------------------------------------------------------- 48 | 49 | sub gen_d { 50 | my (@types) = @_; 51 | 52 | my $d = 'testlibusdt*:::{ '; 53 | my $i = 0; 54 | for my $type (@types) { 55 | if ($type eq 'i') { 56 | $d .= "printf(\"arg$i:'%i' \", args[$i]); "; 57 | } 58 | if ($type eq 'c') { 59 | $d .= "printf(\"arg$i:'%s' \", copyinstr(($user_t)args[$i])); "; 60 | } 61 | $i++; 62 | } 63 | $d .= '}'; 64 | 65 | return $d; 66 | } 67 | 68 | sub run_dtrace { 69 | my ($func, $name, @types) = @_; 70 | my $d = gen_d(@types); 71 | 72 | my @t_cmd; 73 | if (defined $arch) { 74 | @t_cmd = ("./test_usdt$arch", $func, $name, @types); 75 | } 76 | else { 77 | @t_cmd = ("./test_usdt", $func, $name, @types); 78 | } 79 | 80 | my ($d_wtr, $d_rdr, $d_err); 81 | my ($t_wtr, $t_rdr, $t_err); 82 | 83 | $d_err = gensym; 84 | $t_err = gensym; 85 | 86 | #diag(join(' ', @t_cmd)); 87 | my $t_pid = open3($t_wtr, $t_rdr, $t_err, @t_cmd); 88 | my $enabled = $t_rdr->getline; 89 | 90 | my @d_cmd = ('/usr/sbin/dtrace', '-p', $t_pid, '-n', $d); 91 | 92 | #diag(join(' ', @d_cmd)); 93 | my $d_pid = open3($d_wtr, $d_rdr, $d_err, @d_cmd); 94 | my $matched = $d_err->getline; # expect "matched 1 probe" 95 | 96 | $t_wtr->print("go\n"); 97 | $t_wtr->flush; 98 | waitpid( $t_pid, 0 ); 99 | my $t_status = $? >> 8; 100 | 101 | my ($header, $output) = ($d_rdr->getline, $d_rdr->getline); 102 | chomp $header; 103 | chomp $output; 104 | #diag("DTrace header: $header\n"); 105 | #diag("DTrace output: $output\n"); 106 | waitpid( $d_pid, 0 ); 107 | 108 | my $d_status = $? >> 8; 109 | while (!$d_err->eof) { 110 | my $error = $d_err->getline; 111 | chomp $error; 112 | #diag "DTrace error: $error"; 113 | } 114 | 115 | return ($t_status, $d_status, $output || ''); 116 | } 117 | -------------------------------------------------------------------------------- /test_mem_usage.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2012, Chris Andrews. All rights reserved. 3 | */ 4 | 5 | #include "usdt.h" 6 | 7 | #include 8 | #include 9 | #include 10 | 11 | static void 12 | create_and_free_provider(int argc, char **argv) 13 | { 14 | usdt_provider_t *provider; 15 | usdt_probedef_t *probedef; 16 | 17 | if ((provider = usdt_create_provider("testlibusdt", "modname")) == NULL) { 18 | fprintf(stderr, "unable to create provider\n"); 19 | exit (1); 20 | } 21 | if ((probedef = usdt_create_probe((const char *)argv[1], 22 | (const char *)argv[2], 23 | (argc-3), (const char **)&argv[3])) == NULL) 24 | { 25 | fprintf(stderr, "unable to create probe\n"); 26 | exit (1); 27 | } 28 | usdt_provider_add_probe(provider, probedef); 29 | 30 | if ((usdt_provider_enable(provider)) < 0) { 31 | fprintf(stderr, "unable to enable provider: %s\n", usdt_errstr(provider)); 32 | exit (1); 33 | } 34 | 35 | if ((usdt_provider_disable(provider)) < 0) { 36 | fprintf(stderr, "unable to disable provider: %s\n", usdt_errstr(provider)); 37 | exit (1); 38 | } 39 | 40 | usdt_probe_release(probedef); 41 | usdt_provider_free(provider); 42 | } 43 | 44 | int 45 | main(int argc, char **argv) 46 | { 47 | char char_argv[USDT_ARG_MAX]; 48 | int int_argv[USDT_ARG_MAX * 2]; 49 | int i; 50 | char buf[255]; 51 | 52 | for (i = 0; i < USDT_ARG_MAX; i++) 53 | int_argv[i] = i + 1; 54 | for (i = 0; i < USDT_ARG_MAX; i++) 55 | char_argv[i] = (char) i + 65; 56 | 57 | if (argc < 3) { 58 | fprintf(stderr, "usage: %s func name [types ...]\n", argv[0]); 59 | return(1); 60 | } 61 | 62 | for (i = 0; i < USDT_ARG_MAX; i++) { 63 | if (argv[i+3] != NULL && i+3 < argc) { 64 | if (strncmp("c", argv[i+3], 1) == 0) { 65 | argv[i+3] = strdup("char *"); 66 | } 67 | if (strncmp("i", argv[i+3], 1) == 0) { 68 | argv[i+3] = strdup("int"); 69 | } 70 | } 71 | } 72 | 73 | for (i = 0; i < 100000; i++) 74 | create_and_free_provider(argc, argv); 75 | 76 | return 0; 77 | } 78 | -------------------------------------------------------------------------------- /test_usdt.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2012, Chris Andrews. All rights reserved. 3 | */ 4 | 5 | #include "usdt.h" 6 | 7 | #include 8 | #include 9 | #include 10 | 11 | static void 12 | fire_probe(usdt_probedef_t *probedef, int argc, void **argv) 13 | { 14 | if (usdt_is_enabled(probedef->probe)) 15 | usdt_fire_probe(probedef->probe, argc, argv); 16 | } 17 | 18 | int main(int argc, char **argv) { 19 | usdt_provider_t *provider; 20 | usdt_probedef_t *probedef; 21 | char char_argv[USDT_ARG_MAX]; 22 | int int_argv[USDT_ARG_MAX * 2]; 23 | void **args = NULL; 24 | int i; 25 | char buf[255]; 26 | 27 | for (i = 0; i < USDT_ARG_MAX; i++) 28 | int_argv[i] = i + 1; 29 | for (i = 0; i < USDT_ARG_MAX; i++) 30 | char_argv[i] = (char) i + 65; 31 | 32 | if (argc < 3) { 33 | fprintf(stderr, "usage: %s func name [types ...]\n", argv[0]); 34 | return(1); 35 | } 36 | 37 | if (argc > 3) { 38 | args = malloc((argc-3) * sizeof(void *)); 39 | } 40 | 41 | for (i = 0; i < USDT_ARG_MAX; i++) { 42 | if (argv[i+3] != NULL && i+3 < argc) { 43 | if (strncmp("c", argv[i+3], 1) == 0) { 44 | args[i] = (void *)strndup(&char_argv[i], 1); 45 | argv[i+3] = strdup("char *"); 46 | } 47 | if (strncmp("i", argv[i+3], 1) == 0) { 48 | args[i] = (void *)(long)int_argv[i]; 49 | argv[i+3] = strdup("int"); 50 | } 51 | } 52 | } 53 | 54 | if ((provider = usdt_create_provider("testlibusdt", "modname")) == NULL) { 55 | fprintf(stderr, "unable to create provider\n"); 56 | exit (1); 57 | } 58 | if ((probedef = usdt_create_probe((const char *)argv[1], 59 | (const char *)argv[2], 60 | (argc-3), (const char **)&argv[3])) == NULL) 61 | { 62 | fprintf(stderr, "unable to create probe\n"); 63 | exit (1); 64 | } 65 | usdt_provider_add_probe(provider, probedef); 66 | 67 | if ((usdt_provider_enable(provider)) < 0) { 68 | fprintf(stderr, "unable to enable provider: %s\n", usdt_errstr(provider)); 69 | exit (1); 70 | } 71 | 72 | fprintf(stdout, "enabled\n"); 73 | fflush(stdout); 74 | fgets(buf, 255, stdin); 75 | 76 | fire_probe(probedef, (argc-3), (void **)args); 77 | usdt_probe_release(probedef); 78 | 79 | if ((usdt_provider_disable(provider)) < 0) { 80 | fprintf(stderr, "unable to disable provider: %s\n", usdt_errstr(provider)); 81 | exit (1); 82 | } 83 | 84 | usdt_provider_free(provider); 85 | 86 | return 0; 87 | } 88 | -------------------------------------------------------------------------------- /usdt.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2012, Chris Andrews. All rights reserved. 3 | */ 4 | 5 | #include "usdt_internal.h" 6 | 7 | #include 8 | #include 9 | #include 10 | 11 | char *usdt_errors[] = { 12 | "failed to allocate memory", 13 | "failed to allocate page-aligned memory", 14 | "no probes defined", 15 | "failed to load DOF: %s", 16 | "provider is already enabled", 17 | "failed to unload DOF: %s", 18 | "probe named %s:%s:%s:%s already exists", 19 | "failed to remove probe %s:%s:%s:%s" 20 | }; 21 | 22 | static void 23 | free_probedef(usdt_probedef_t *pd) 24 | { 25 | int i; 26 | 27 | switch (pd->refcnt) { 28 | case 1: 29 | free((char *)pd->function); 30 | free((char *)pd->name); 31 | if (pd->probe) { 32 | usdt_free_tracepoints(pd->probe); 33 | free(pd->probe); 34 | } 35 | for (i = 0; i < pd->argc; i++) 36 | free(pd->types[i]); 37 | free(pd); 38 | break; 39 | case 2: 40 | pd->refcnt = 1; 41 | break; 42 | default: 43 | break; 44 | } 45 | } 46 | 47 | usdt_provider_t * 48 | usdt_create_provider(const char *name, const char *module) 49 | { 50 | usdt_provider_t *provider; 51 | 52 | if ((provider = malloc(sizeof *provider)) == NULL) 53 | return NULL; 54 | 55 | provider->name = strdup(name); 56 | provider->module = strdup(module); 57 | provider->probedefs = NULL; 58 | provider->enabled = 0; 59 | 60 | return provider; 61 | } 62 | 63 | usdt_probedef_t * 64 | usdt_create_probe(const char *func, const char *name, size_t argc, const char **types) 65 | { 66 | int i; 67 | usdt_probedef_t *p; 68 | 69 | if (argc > USDT_ARG_MAX) 70 | argc = USDT_ARG_MAX; 71 | 72 | if ((p = malloc(sizeof *p)) == NULL) 73 | return (NULL); 74 | 75 | p->refcnt = 2; 76 | p->function = strdup(func); 77 | p->name = strdup(name); 78 | p->argc = argc; 79 | p->probe = NULL; 80 | 81 | for (i = 0; i < argc; i++) 82 | p->types[i] = strdup(types[i]); 83 | 84 | return (p); 85 | } 86 | 87 | void 88 | usdt_probe_release(usdt_probedef_t *probedef) 89 | { 90 | free_probedef(probedef); 91 | } 92 | 93 | int 94 | usdt_provider_add_probe(usdt_provider_t *provider, usdt_probedef_t *probedef) 95 | { 96 | usdt_probedef_t *pd; 97 | 98 | if (provider->probedefs != NULL) { 99 | for (pd = provider->probedefs; (pd != NULL); pd = pd->next) { 100 | if ((strcmp(pd->name, probedef->name) == 0) && 101 | (strcmp(pd->function, probedef->function) == 0)) { 102 | usdt_error(provider, USDT_ERROR_DUP_PROBE, 103 | provider->name, provider->module, 104 | probedef->function, probedef->name); 105 | return (-1); 106 | } 107 | } 108 | } 109 | 110 | probedef->next = NULL; 111 | if (provider->probedefs == NULL) 112 | provider->probedefs = probedef; 113 | else { 114 | for (pd = provider->probedefs; (pd->next != NULL); pd = pd->next) ; 115 | pd->next = probedef; 116 | } 117 | 118 | return (0); 119 | } 120 | 121 | int 122 | usdt_provider_remove_probe(usdt_provider_t *provider, usdt_probedef_t *probedef) 123 | { 124 | usdt_probedef_t *pd, *prev_pd = NULL; 125 | 126 | if (provider->probedefs == NULL) { 127 | usdt_error(provider, USDT_ERROR_NOPROBES); 128 | return (-1); 129 | } 130 | 131 | for (pd = provider->probedefs; (pd != NULL); 132 | prev_pd = pd, pd = pd->next) { 133 | 134 | if ((strcmp(pd->name, probedef->name) == 0) && 135 | (strcmp(pd->function, probedef->function) == 0)) { 136 | 137 | if (prev_pd == NULL) 138 | provider->probedefs = pd->next; 139 | else 140 | prev_pd->next = pd->next; 141 | 142 | return (0); 143 | } 144 | } 145 | 146 | usdt_error(provider, USDT_ERROR_REMOVE_PROBE, 147 | provider->name, provider->module, 148 | probedef->function, probedef->name); 149 | return (-1); 150 | } 151 | 152 | int 153 | usdt_provider_enable(usdt_provider_t *provider) 154 | { 155 | usdt_strtab_t strtab; 156 | usdt_dof_file_t *file; 157 | usdt_probedef_t *pd; 158 | int i; 159 | size_t size; 160 | usdt_dof_section_t sects[5]; 161 | 162 | if (provider->enabled == 1) { 163 | usdt_error(provider, USDT_ERROR_ALREADYENABLED); 164 | return (0); /* not fatal */ 165 | } 166 | 167 | if (provider->probedefs == NULL) { 168 | usdt_error(provider, USDT_ERROR_NOPROBES); 169 | return (-1); 170 | } 171 | 172 | for (pd = provider->probedefs; pd != NULL; pd = pd->next) { 173 | if ((pd->probe = malloc(sizeof(*pd->probe))) == NULL) { 174 | usdt_error(provider, USDT_ERROR_MALLOC); 175 | return (-1); 176 | } 177 | } 178 | 179 | if ((usdt_strtab_init(&strtab, 0)) < 0) { 180 | usdt_error(provider, USDT_ERROR_MALLOC); 181 | return (-1); 182 | } 183 | 184 | if ((usdt_strtab_add(&strtab, provider->name)) == 0) { 185 | usdt_error(provider, USDT_ERROR_MALLOC); 186 | return (-1); 187 | } 188 | 189 | if ((usdt_dof_probes_sect(§s[0], provider, &strtab)) < 0) 190 | return (-1); 191 | if ((usdt_dof_prargs_sect(§s[1], provider)) < 0) 192 | return (-1); 193 | 194 | size = usdt_provider_dof_size(provider, &strtab); 195 | if ((file = usdt_dof_file_init(provider, size)) == NULL) 196 | return (-1); 197 | 198 | if ((usdt_dof_proffs_sect(§s[2], provider, file->dof)) < 0) 199 | return (-1); 200 | if ((usdt_dof_prenoffs_sect(§s[3], provider, file->dof)) < 0) 201 | return (-1); 202 | if ((usdt_dof_provider_sect(§s[4], provider)) < 0) 203 | return (-1); 204 | 205 | for (i = 0; i < 5; i++) 206 | usdt_dof_file_append_section(file, §s[i]); 207 | 208 | usdt_dof_file_generate(file, &strtab); 209 | 210 | usdt_dof_section_free((usdt_dof_section_t *)&strtab); 211 | for (i = 0; i < 5; i++) 212 | usdt_dof_section_free(§s[i]); 213 | 214 | if ((usdt_dof_file_load(file, provider->module)) < 0) { 215 | usdt_error(provider, USDT_ERROR_LOADDOF, strerror(errno)); 216 | return (-1); 217 | } 218 | 219 | provider->enabled = 1; 220 | provider->file = file; 221 | 222 | return (0); 223 | } 224 | 225 | int 226 | usdt_provider_disable(usdt_provider_t *provider) 227 | { 228 | usdt_probedef_t *pd; 229 | 230 | if (provider->enabled == 0) 231 | return (0); 232 | 233 | if ((usdt_dof_file_unload((usdt_dof_file_t *)provider->file)) < 0) { 234 | usdt_error(provider, USDT_ERROR_UNLOADDOF, strerror(errno)); 235 | return (-1); 236 | } 237 | 238 | usdt_dof_file_free(provider->file); 239 | provider->file = NULL; 240 | 241 | /* We would like to free the tracepoints here too, but OS X 242 | * (and to a lesser extent Illumos) struggle with this: 243 | * 244 | * If a provider is repeatedly disabled and re-enabled, and is 245 | * allowed to reuse the same memory for its tracepoints, *and* 246 | * there's a DTrace consumer running with enablings for these 247 | * probes, tracepoints are not always cleaned up sufficiently 248 | * that the newly-created probes work. 249 | * 250 | * Here, then, we will leak the memory holding the 251 | * tracepoints, which serves to stop us reusing the same 252 | * memory address for new tracepoints, avoiding the bug. 253 | */ 254 | 255 | for (pd = provider->probedefs; (pd != NULL); pd = pd->next) { 256 | /* may have an as yet never-enabled probe on an 257 | otherwise enabled provider */ 258 | if (pd->probe) { 259 | /* usdt_free_tracepoints(pd->probe); */ 260 | free(pd->probe); 261 | pd->probe = NULL; 262 | } 263 | } 264 | 265 | provider->enabled = 0; 266 | 267 | return (0); 268 | } 269 | 270 | void 271 | usdt_provider_free(usdt_provider_t *provider) 272 | { 273 | usdt_probedef_t *pd, *next; 274 | 275 | for (pd = provider->probedefs; pd != NULL; pd = next) { 276 | next = pd->next; 277 | free_probedef(pd); 278 | } 279 | 280 | free((char *)provider->name); 281 | free((char *)provider->module); 282 | free(provider); 283 | } 284 | 285 | int 286 | usdt_is_enabled(usdt_probe_t *probe) 287 | { 288 | if (probe != NULL) 289 | return (*probe->isenabled_addr)(); 290 | else 291 | return 0; 292 | } 293 | 294 | void 295 | usdt_fire_probe(usdt_probe_t *probe, size_t argc, void **nargv) 296 | { 297 | if (probe != NULL) 298 | usdt_probe_args(probe->probe_addr, argc, nargv); 299 | } 300 | 301 | static void 302 | usdt_verror(usdt_provider_t *provider, usdt_error_t error, va_list argp) 303 | { 304 | vasprintf(&provider->error, usdt_errors[error], argp); 305 | } 306 | 307 | void 308 | usdt_error(usdt_provider_t *provider, usdt_error_t error, ...) 309 | { 310 | va_list argp; 311 | 312 | va_start(argp, error); 313 | usdt_verror(provider, error, argp); 314 | va_end(argp); 315 | } 316 | 317 | char * 318 | usdt_errstr(usdt_provider_t *provider) 319 | { 320 | return (provider->error); 321 | } 322 | -------------------------------------------------------------------------------- /usdt.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2012, Chris Andrews. All rights reserved. 3 | */ 4 | 5 | #include 6 | #include 7 | 8 | #define USDT_ARG_MAX 32 9 | 10 | typedef enum usdt_error { 11 | USDT_ERROR_MALLOC = 0, 12 | USDT_ERROR_VALLOC, 13 | USDT_ERROR_NOPROBES, 14 | USDT_ERROR_LOADDOF, 15 | USDT_ERROR_ALREADYENABLED, 16 | USDT_ERROR_UNLOADDOF, 17 | USDT_ERROR_DUP_PROBE, 18 | USDT_ERROR_REMOVE_PROBE 19 | } usdt_error_t; 20 | 21 | typedef struct usdt_probe { 22 | int (*isenabled_addr)(void); 23 | void *probe_addr; 24 | } usdt_probe_t; 25 | 26 | int usdt_is_enabled(usdt_probe_t *probe); 27 | void usdt_fire_probe(usdt_probe_t *probe, size_t argc, void **argv); 28 | 29 | typedef struct usdt_probedef { 30 | const char *name; 31 | const char *function; 32 | size_t argc; 33 | char *types[USDT_ARG_MAX]; 34 | struct usdt_probe *probe; 35 | struct usdt_probedef *next; 36 | int refcnt; 37 | } usdt_probedef_t; 38 | 39 | usdt_probedef_t *usdt_create_probe(const char *func, const char *name, 40 | size_t argc, const char **types); 41 | void usdt_probe_release(usdt_probedef_t *probedef); 42 | 43 | typedef struct usdt_provider { 44 | const char *name; 45 | const char *module; 46 | usdt_probedef_t *probedefs; 47 | char *error; 48 | int enabled; 49 | void *file; 50 | } usdt_provider_t; 51 | 52 | usdt_provider_t *usdt_create_provider(const char *name, const char *module); 53 | int usdt_provider_add_probe(usdt_provider_t *provider, usdt_probedef_t *probedef); 54 | int usdt_provider_remove_probe(usdt_provider_t *provider, usdt_probedef_t *probedef); 55 | int usdt_provider_enable(usdt_provider_t *provider); 56 | int usdt_provider_disable(usdt_provider_t *provider); 57 | void usdt_provider_free(usdt_provider_t *provider); 58 | 59 | void usdt_error(usdt_provider_t *provider, usdt_error_t error, ...); 60 | char *usdt_errstr(usdt_provider_t *provider); 61 | 62 | -------------------------------------------------------------------------------- /usdt_dof.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2012, Chris Andrews. All rights reserved. 3 | */ 4 | 5 | #include "usdt_internal.h" 6 | 7 | int 8 | usdt_dof_section_add_data(usdt_dof_section_t *section, void *data, size_t length) 9 | { 10 | int newlen = section->size + length; 11 | 12 | if ((section->data = realloc((char *)section->data, newlen)) == NULL) 13 | return (-1); 14 | 15 | memcpy(section->data + section->size, data, length); 16 | section->size = newlen; 17 | return (0); 18 | } 19 | 20 | size_t 21 | usdt_provider_dof_size(usdt_provider_t *provider, usdt_strtab_t *strtab) 22 | { 23 | uint8_t i, j; 24 | int args = 0; 25 | int probes = 0; 26 | size_t size = 0; 27 | usdt_probedef_t *pd; 28 | size_t sections[8]; 29 | 30 | for (pd = provider->probedefs; pd != NULL; pd = pd->next) { 31 | args += pd->argc; 32 | probes++; 33 | } 34 | 35 | sections[0] = sizeof(dof_hdr_t); 36 | sections[1] = sizeof(dof_sec_t) * 6; 37 | sections[2] = strtab->size; 38 | sections[3] = sizeof(dof_probe_t) * probes; 39 | sections[4] = sizeof(uint8_t) * args; 40 | sections[5] = sizeof(uint32_t) * probes; 41 | sections[6] = sizeof(uint32_t) * probes; 42 | sections[7] = sizeof(dof_provider_t); 43 | 44 | for (i = 0; i < 8; i++) { 45 | size += sections[i]; 46 | j = size % 8; 47 | if (j > 0) 48 | size += (8 - j); 49 | } 50 | 51 | return size; 52 | } 53 | 54 | int 55 | usdt_dof_section_init(usdt_dof_section_t *section, uint32_t type, dof_secidx_t index) 56 | { 57 | section->type = type; 58 | section->index = index; 59 | section->flags = DOF_SECF_LOAD; 60 | section->offset = 0; 61 | section->size = 0; 62 | section->entsize = 0; 63 | section->pad = 0; 64 | section->next = NULL; 65 | 66 | if ((section->data = malloc(1)) == NULL) 67 | return (-1); 68 | 69 | switch(type) { 70 | case DOF_SECT_PROBES: section->align = 8; break; 71 | case DOF_SECT_PRARGS: section->align = 1; break; 72 | case DOF_SECT_PROFFS: section->align = 4; break; 73 | case DOF_SECT_PRENOFFS: section->align = 4; break; 74 | case DOF_SECT_PROVIDER: section->align = 4; break; 75 | } 76 | 77 | return (0); 78 | } 79 | 80 | void 81 | usdt_dof_section_free(usdt_dof_section_t *section) 82 | { 83 | free(section->data); 84 | } 85 | 86 | int 87 | usdt_strtab_init(usdt_strtab_t *strtab, dof_secidx_t index) 88 | { 89 | strtab->type = DOF_SECT_STRTAB;; 90 | strtab->index = index; 91 | strtab->flags = DOF_SECF_LOAD; 92 | strtab->offset = 0; 93 | strtab->size = 0; 94 | strtab->entsize = 0; 95 | strtab->pad = 0; 96 | strtab->data = NULL; 97 | strtab->align = 1; 98 | strtab->strindex = 1; 99 | 100 | if ((strtab->data = (char *) malloc(1)) == NULL) 101 | return (-1); 102 | 103 | *strtab->data = '\0'; 104 | 105 | return (0); 106 | } 107 | 108 | dof_stridx_t 109 | usdt_strtab_add(usdt_strtab_t *strtab, const char *string) 110 | { 111 | size_t length; 112 | int index; 113 | 114 | length = strlen(string); 115 | index = strtab->strindex; 116 | strtab->strindex += (length + 1); 117 | 118 | if ((strtab->data = realloc(strtab->data, strtab->strindex)) == NULL) 119 | return (0); 120 | 121 | memcpy((char *) (strtab->data + index), (char *)string, length + 1); 122 | strtab->size = index + length + 1; 123 | 124 | return (index); 125 | } 126 | 127 | -------------------------------------------------------------------------------- /usdt_dof_file.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2012, Chris Andrews. All rights reserved. 3 | */ 4 | 5 | #include "usdt_internal.h" 6 | 7 | #include 8 | 9 | static uint8_t 10 | dof_version(uint8_t header_version) 11 | { 12 | uint8_t dof_version; 13 | /* DOF versioning: Apple always needs version 3, but Solaris can use 14 | 1 or 2 depending on whether is-enabled probes are needed. */ 15 | #ifdef __APPLE__ 16 | dof_version = DOF_VERSION_3; 17 | #else 18 | switch(header_version) { 19 | case 1: 20 | dof_version = DOF_VERSION_1; 21 | break; 22 | case 2: 23 | dof_version = DOF_VERSION_2; 24 | break; 25 | default: 26 | dof_version = DOF_VERSION; 27 | } 28 | #endif 29 | return dof_version; 30 | } 31 | 32 | #ifdef __APPLE__ 33 | static const char *helper = "/dev/dtracehelper"; 34 | 35 | static int 36 | load_dof(int fd, dof_helper_t *dh) 37 | { 38 | int ret; 39 | uint8_t buffer[sizeof(dof_ioctl_data_t) + sizeof(dof_helper_t)]; 40 | dof_ioctl_data_t* ioctlData = (dof_ioctl_data_t*)buffer; 41 | user_addr_t val; 42 | 43 | ioctlData->dofiod_count = 1; 44 | memcpy(&ioctlData->dofiod_helpers[0], dh, sizeof(dof_helper_t)); 45 | 46 | val = (user_addr_t)(unsigned long)ioctlData; 47 | ret = ioctl(fd, DTRACEHIOC_ADDDOF, &val); 48 | 49 | if (ret < 0) 50 | return ret; 51 | 52 | return (int)(ioctlData->dofiod_helpers[0].dofhp_dof); 53 | } 54 | 55 | #else /* Solaris and FreeBSD */ 56 | 57 | /* ignore Sol10 GA ... */ 58 | static const char *helper = "/dev/dtrace/helper"; 59 | 60 | static int 61 | load_dof(int fd, dof_helper_t *dh) 62 | { 63 | int ret; 64 | 65 | ret = ioctl(fd, DTRACEHIOC_ADDDOF, dh); 66 | #if defined(__FreeBSD__) && __FreeBSD__ <= 10 67 | if (ret != -1) 68 | ret = dh ->gen; 69 | #endif 70 | return ret; 71 | } 72 | 73 | #endif 74 | 75 | static void 76 | pad_section(usdt_dof_section_t *sec) 77 | { 78 | size_t i, pad; 79 | 80 | if (sec->align > 1) { 81 | i = sec->offset % sec->align; 82 | if (i > 0) { 83 | pad = sec->align - i; 84 | sec->offset = (pad + sec->offset); 85 | sec->pad = pad; 86 | } 87 | } 88 | } 89 | 90 | static void 91 | dof_header(dof_hdr_t *header) 92 | { 93 | int i; 94 | 95 | header->dofh_ident[DOF_ID_MAG0] = DOF_MAG_MAG0; 96 | header->dofh_ident[DOF_ID_MAG1] = DOF_MAG_MAG1; 97 | header->dofh_ident[DOF_ID_MAG2] = DOF_MAG_MAG2; 98 | header->dofh_ident[DOF_ID_MAG3] = DOF_MAG_MAG3; 99 | 100 | header->dofh_ident[DOF_ID_MODEL] = DOF_MODEL_NATIVE; 101 | header->dofh_ident[DOF_ID_ENCODING] = DOF_ENCODE_NATIVE; 102 | header->dofh_ident[DOF_ID_VERSION] = dof_version(2); 103 | header->dofh_ident[DOF_ID_DIFVERS] = DIF_VERSION; 104 | header->dofh_ident[DOF_ID_DIFIREG] = DIF_DIR_NREGS; 105 | header->dofh_ident[DOF_ID_DIFTREG] = DIF_DTR_NREGS; 106 | 107 | for (i = DOF_ID_PAD; i < DOF_ID_SIZE; i++) 108 | header->dofh_ident[i] = 0; 109 | 110 | header->dofh_flags = 0; 111 | 112 | header->dofh_hdrsize = sizeof(dof_hdr_t); 113 | header->dofh_secsize = sizeof(dof_sec_t); 114 | header->dofh_secoff = sizeof(dof_hdr_t); 115 | 116 | header->dofh_loadsz = 0; 117 | header->dofh_filesz = 0; 118 | header->dofh_pad = 0; 119 | } 120 | 121 | static size_t 122 | add_header(usdt_dof_file_t *file, size_t offset, usdt_dof_section_t *section) 123 | { 124 | dof_sec_t header; 125 | 126 | header.dofs_flags = section->flags; 127 | header.dofs_type = section->type; 128 | header.dofs_offset = section->offset; 129 | header.dofs_size = section->size; 130 | header.dofs_entsize = section->entsize; 131 | header.dofs_align = section->align; 132 | 133 | memcpy((file->dof + offset), &header, sizeof(dof_sec_t)); 134 | return (offset + sizeof(dof_sec_t)); 135 | } 136 | 137 | static size_t 138 | add_section(usdt_dof_file_t *file, size_t offset, usdt_dof_section_t *section) 139 | { 140 | if (section->pad > 0) { 141 | /* maximum padding required is 7 */ 142 | memcpy((file->dof + offset), "\0\0\0\0\0\0\0", section->pad); 143 | offset += section->pad; 144 | } 145 | 146 | memcpy((file->dof + offset), section->data, section->size); 147 | return (offset + section->size); 148 | } 149 | 150 | int 151 | usdt_dof_file_unload(usdt_dof_file_t *file) 152 | { 153 | int fd, ret; 154 | 155 | if ((fd = open(helper, O_RDWR)) < 0) 156 | return (-1); 157 | 158 | #ifdef __FreeBSD__ 159 | ret = ioctl(fd, DTRACEHIOC_REMOVE, &file->gen); 160 | #else 161 | ret = ioctl(fd, DTRACEHIOC_REMOVE, file->gen); 162 | #endif 163 | 164 | if (ret < 0) 165 | return (-1); 166 | 167 | if ((close(fd)) < 0) 168 | return (-1); 169 | 170 | return (0); 171 | } 172 | 173 | int 174 | usdt_dof_file_load(usdt_dof_file_t *file, const char *module) 175 | { 176 | dof_helper_t dh; 177 | dof_hdr_t *dof; 178 | int fd; 179 | 180 | dof = (dof_hdr_t *) file->dof; 181 | 182 | dh.dofhp_dof = (uintptr_t)dof; 183 | dh.dofhp_addr = (uintptr_t)dof; 184 | #if __FreeBSD__ >= 11 185 | dh.dofhp_pid = getpid(); 186 | #endif 187 | (void) strncpy(dh.dofhp_mod, module, sizeof (dh.dofhp_mod)); 188 | 189 | if ((fd = open(helper, O_RDWR)) < 0) 190 | return (-1); 191 | 192 | file->gen = load_dof(fd, &dh); 193 | 194 | if ((close(fd)) < 0) 195 | return (-1); 196 | 197 | if (file->gen < 0) 198 | return (-1); 199 | 200 | return (0); 201 | } 202 | 203 | void 204 | usdt_dof_file_append_section(usdt_dof_file_t *file, usdt_dof_section_t *section) 205 | { 206 | usdt_dof_section_t *s; 207 | 208 | if (file->sections == NULL) { 209 | file->sections = section; 210 | } 211 | else { 212 | for (s = file->sections; (s->next != NULL); s = s->next) ; 213 | s->next = section; 214 | } 215 | } 216 | 217 | void 218 | usdt_dof_file_generate(usdt_dof_file_t *file, usdt_strtab_t *strtab) 219 | { 220 | dof_hdr_t header; 221 | uint64_t filesz; 222 | uint64_t loadsz; 223 | usdt_dof_section_t *sec; 224 | size_t offset; 225 | 226 | dof_header(&header); 227 | header.dofh_secnum = 6; 228 | 229 | filesz = sizeof(dof_hdr_t) + (sizeof(dof_sec_t) * header.dofh_secnum); 230 | loadsz = filesz; 231 | 232 | strtab->offset = filesz; 233 | pad_section((usdt_dof_section_t *)strtab); 234 | filesz += strtab->size + strtab->pad; 235 | 236 | if (strtab->flags & 1) 237 | loadsz += strtab->size + strtab->pad; 238 | 239 | for (sec = file->sections; sec != NULL; sec = sec->next) { 240 | sec->offset = filesz; 241 | pad_section(sec); 242 | filesz += sec->size + sec->pad; 243 | if (sec->flags & 1) 244 | loadsz += sec->size + sec->pad; 245 | } 246 | 247 | header.dofh_loadsz = loadsz; 248 | header.dofh_filesz = filesz; 249 | memcpy(file->dof, &header, sizeof(dof_hdr_t)); 250 | 251 | offset = sizeof(dof_hdr_t); 252 | 253 | offset = add_header(file, offset, (usdt_dof_section_t *)strtab); 254 | 255 | for (sec = file->sections; sec != NULL; sec = sec->next) 256 | offset = add_header(file, offset, sec); 257 | 258 | offset = add_section(file, offset, (usdt_dof_section_t *)strtab); 259 | 260 | for (sec = file->sections; sec != NULL; sec = sec->next) 261 | offset = add_section(file, offset, sec); 262 | } 263 | 264 | usdt_dof_file_t * 265 | usdt_dof_file_init(usdt_provider_t *provider, size_t size) 266 | { 267 | usdt_dof_file_t *file; 268 | 269 | if ((file = malloc(sizeof(*file))) == NULL) { 270 | usdt_error(provider, USDT_ERROR_MALLOC); 271 | return (NULL); 272 | } 273 | 274 | if ((file->dof = valloc(size)) == NULL) { 275 | usdt_error(provider, USDT_ERROR_VALLOC); 276 | return (NULL); 277 | } 278 | 279 | file->sections = NULL; 280 | file->size = size; 281 | 282 | return (file); 283 | } 284 | 285 | void 286 | usdt_dof_file_free(usdt_dof_file_t *file) 287 | { 288 | free(file->dof); 289 | free(file); 290 | } 291 | -------------------------------------------------------------------------------- /usdt_dof_sections.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2012, Chris Andrews. All rights reserved. 3 | */ 4 | 5 | #include "usdt_internal.h" 6 | 7 | int 8 | usdt_dof_probes_sect(usdt_dof_section_t *probes, 9 | usdt_provider_t *provider, usdt_strtab_t *strtab) 10 | { 11 | usdt_probedef_t *pd; 12 | dof_probe_t p; 13 | dof_stridx_t type, argv; 14 | uint8_t argc, i; 15 | uint32_t argidx = 0; 16 | uint32_t offidx = 0; 17 | 18 | usdt_dof_section_init(probes, DOF_SECT_PROBES, 1); 19 | 20 | for (pd = provider->probedefs; pd != NULL; pd = pd->next) { 21 | argc = 0; 22 | argv = 0; 23 | type = 0; 24 | 25 | for (i = 0; i < pd->argc; i++) { 26 | type = usdt_strtab_add(strtab, pd->types[i]); 27 | argc++; 28 | if (argv == 0) 29 | argv = type; 30 | } 31 | 32 | if (usdt_create_tracepoints(pd->probe) < 0) { 33 | usdt_error(provider, USDT_ERROR_VALLOC); 34 | return (-1); 35 | } 36 | 37 | #ifdef __x86_64__ 38 | p.dofpr_addr = (uint64_t) pd->probe->isenabled_addr; 39 | #elif __i386__ || __i386 40 | p.dofpr_addr = (uint32_t) pd->probe->isenabled_addr; 41 | #else 42 | #error "only x86_64 and i386 supported" 43 | #endif 44 | p.dofpr_func = usdt_strtab_add(strtab, pd->function); 45 | p.dofpr_name = usdt_strtab_add(strtab, pd->name); 46 | p.dofpr_nargv = argv; 47 | p.dofpr_xargv = argv; 48 | p.dofpr_argidx = argidx; 49 | p.dofpr_offidx = offidx; 50 | p.dofpr_nargc = argc; 51 | p.dofpr_xargc = argc; 52 | p.dofpr_noffs = 1; 53 | p.dofpr_enoffidx = offidx; 54 | p.dofpr_nenoffs = 1; 55 | p.dofpr_pad1 = 0; 56 | p.dofpr_pad2 = 0; 57 | 58 | usdt_dof_section_add_data(probes, &p, sizeof(dof_probe_t)); 59 | probes->entsize = sizeof(dof_probe_t); 60 | 61 | argidx += argc; 62 | offidx++; 63 | } 64 | 65 | return (0); 66 | } 67 | 68 | int 69 | usdt_dof_prargs_sect(usdt_dof_section_t *prargs, usdt_provider_t *provider) 70 | { 71 | usdt_probedef_t *pd; 72 | uint8_t i; 73 | 74 | usdt_dof_section_init(prargs, DOF_SECT_PRARGS, 2); 75 | prargs->entsize = 1; 76 | 77 | for (pd = provider->probedefs; pd != NULL; pd = pd->next) { 78 | for (i = 0; i < pd->argc; i++) 79 | usdt_dof_section_add_data(prargs, &i, 1); 80 | } 81 | if (prargs->size == 0) { 82 | i = 0; 83 | if (usdt_dof_section_add_data(prargs, &i, 1) < 0) { 84 | usdt_error(provider, USDT_ERROR_MALLOC); 85 | return (-1); 86 | } 87 | } 88 | 89 | return (0); 90 | } 91 | 92 | int 93 | usdt_dof_proffs_sect(usdt_dof_section_t *proffs, 94 | usdt_provider_t *provider, char *dof) 95 | { 96 | usdt_probedef_t *pd; 97 | uint32_t off; 98 | 99 | usdt_dof_section_init(proffs, DOF_SECT_PROFFS, 3); 100 | proffs->entsize = 4; 101 | 102 | for (pd = provider->probedefs; pd != NULL; pd = pd->next) { 103 | off = usdt_probe_offset(pd->probe, dof, pd->argc); 104 | if (usdt_dof_section_add_data(proffs, &off, 4) < 0) { 105 | usdt_error(provider, USDT_ERROR_MALLOC); 106 | return (-1); 107 | } 108 | } 109 | 110 | return (0); 111 | } 112 | 113 | int 114 | usdt_dof_prenoffs_sect(usdt_dof_section_t *prenoffs, 115 | usdt_provider_t *provider, char *dof) 116 | { 117 | usdt_probedef_t *pd; 118 | uint32_t off; 119 | 120 | usdt_dof_section_init(prenoffs, DOF_SECT_PRENOFFS, 4); 121 | prenoffs->entsize = 4; 122 | 123 | for (pd = provider->probedefs; pd != NULL; pd = pd->next) { 124 | off = usdt_is_enabled_offset(pd->probe, dof); 125 | if (usdt_dof_section_add_data(prenoffs, &off, 4) < 0) { 126 | usdt_error(provider, USDT_ERROR_MALLOC); 127 | return (-1); 128 | } 129 | } 130 | 131 | return (0); 132 | } 133 | 134 | int 135 | usdt_dof_provider_sect(usdt_dof_section_t *provider_s, usdt_provider_t *provider) 136 | { 137 | dof_provider_t p; 138 | 139 | usdt_dof_section_init(provider_s, DOF_SECT_PROVIDER, 5); 140 | 141 | p.dofpv_strtab = 0; 142 | p.dofpv_probes = 1; 143 | p.dofpv_prargs = 2; 144 | p.dofpv_proffs = 3; 145 | p.dofpv_prenoffs = 4; 146 | p.dofpv_name = 1; /* provider name always first strtab entry. */ 147 | 148 | /* 149 | * Stability is something of a hack. Everything is marked * 150 | * "stable" here to permit use of the "args" array, which is * 151 | * needed to access arguments past "arg9". 152 | * 153 | * It should be up to the creator of the provider to decide 154 | * this, though, and it should be possible to set the 155 | * appropriate stability at creation time. 156 | */ 157 | 158 | p.dofpv_provattr = DOF_ATTR(DTRACE_STABILITY_STABLE, 159 | DTRACE_STABILITY_STABLE, 160 | DTRACE_STABILITY_STABLE); 161 | p.dofpv_modattr = DOF_ATTR(DTRACE_STABILITY_STABLE, 162 | DTRACE_STABILITY_STABLE, 163 | DTRACE_STABILITY_STABLE); 164 | p.dofpv_funcattr = DOF_ATTR(DTRACE_STABILITY_STABLE, 165 | DTRACE_STABILITY_STABLE, 166 | DTRACE_STABILITY_STABLE); 167 | p.dofpv_nameattr = DOF_ATTR(DTRACE_STABILITY_STABLE, 168 | DTRACE_STABILITY_STABLE, 169 | DTRACE_STABILITY_STABLE); 170 | p.dofpv_argsattr = DOF_ATTR(DTRACE_STABILITY_STABLE, 171 | DTRACE_STABILITY_STABLE, 172 | DTRACE_STABILITY_STABLE); 173 | 174 | if ((usdt_dof_section_add_data(provider_s, &p, sizeof(p))) < 0) { 175 | usdt_error(provider, USDT_ERROR_MALLOC); 176 | return (-1); 177 | } 178 | 179 | return (0); 180 | } 181 | -------------------------------------------------------------------------------- /usdt_internal.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2012, Chris Andrews. All rights reserved. 3 | */ 4 | 5 | #ifdef __linux__ 6 | #include 7 | #if __BYTE_ORDER == __LITTLE_ENDIAN 8 | #ifndef _LITTLE_ENDIAN 9 | #define _LITTLE_ENDIAN 10 | #endif 11 | #endif 12 | #endif 13 | 14 | #include 15 | #include 16 | #include 17 | 18 | #include 19 | #include 20 | #include 21 | #include 22 | #include 23 | #include 24 | #include 25 | 26 | #define FUNC_SIZE 32 27 | 28 | #include "usdt.h" 29 | 30 | extern void usdt_tracepoint_isenabled(void); 31 | extern void usdt_tracepoint_probe(void); 32 | extern void usdt_tracepoint_end(void); 33 | extern void usdt_probe_args(void *, int, void**); 34 | 35 | uint32_t usdt_probe_offset(usdt_probe_t *probe, char *dof, uint8_t argc); 36 | uint32_t usdt_is_enabled_offset(usdt_probe_t *probe, char *dof); 37 | int usdt_create_tracepoints(usdt_probe_t *probe); 38 | void usdt_free_tracepoints(usdt_probe_t *probe); 39 | 40 | typedef struct usdt_dof_section { 41 | dof_secidx_t index; 42 | uint32_t type; 43 | uint32_t flags; 44 | uint32_t align; 45 | uint64_t offset; 46 | uint64_t size; 47 | uint32_t entsize; 48 | size_t pad; 49 | struct usdt_dof_section *next; 50 | char *data; 51 | } usdt_dof_section_t; 52 | 53 | int usdt_dof_section_init(usdt_dof_section_t *section, 54 | uint32_t type, dof_secidx_t index); 55 | int usdt_dof_section_add_data(usdt_dof_section_t *section, 56 | void *data, size_t length); 57 | void usdt_dof_section_free(usdt_dof_section_t *section); 58 | 59 | typedef struct usdt_strtab { 60 | dof_secidx_t index; 61 | uint32_t type; 62 | uint32_t flags; 63 | uint32_t align; 64 | uint64_t offset; 65 | uint64_t size; 66 | uint32_t entsize; 67 | size_t pad; 68 | int strindex; 69 | char *data; 70 | } usdt_strtab_t; 71 | 72 | int usdt_strtab_init(usdt_strtab_t *strtab, dof_secidx_t index); 73 | dof_stridx_t usdt_strtab_add(usdt_strtab_t *strtab, const char *string); 74 | char *usdt_strtab_header(usdt_strtab_t *strtab); 75 | size_t usdt_strtab_size(usdt_strtab_t *strtab); 76 | 77 | size_t usdt_provider_dof_size(usdt_provider_t *provider, usdt_strtab_t *strtab); 78 | 79 | typedef struct usdt_dof_file { 80 | char *dof; 81 | int gen; 82 | size_t size; 83 | usdt_dof_section_t *sections; 84 | } usdt_dof_file_t; 85 | 86 | usdt_dof_file_t *usdt_dof_file_init(usdt_provider_t *provider, size_t size); 87 | void usdt_dof_file_append_section(usdt_dof_file_t *file, usdt_dof_section_t *section); 88 | void usdt_dof_file_generate(usdt_dof_file_t *file, usdt_strtab_t *strtab); 89 | int usdt_dof_file_load(usdt_dof_file_t *file, const char *module); 90 | int usdt_dof_file_unload(usdt_dof_file_t *file); 91 | void usdt_dof_file_free(usdt_dof_file_t *file); 92 | 93 | int usdt_dof_probes_sect(usdt_dof_section_t *probes, 94 | usdt_provider_t *provider, usdt_strtab_t *strtab); 95 | int usdt_dof_prargs_sect(usdt_dof_section_t *prargs, 96 | usdt_provider_t *provider); 97 | int usdt_dof_proffs_sect(usdt_dof_section_t *proffs, 98 | usdt_provider_t *provider, char *dof); 99 | int usdt_dof_prenoffs_sect(usdt_dof_section_t *prenoffs, 100 | usdt_provider_t *provider, char *dof); 101 | int usdt_dof_provider_sect(usdt_dof_section_t *provider_s, 102 | usdt_provider_t *provider); 103 | 104 | -------------------------------------------------------------------------------- /usdt_probe.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2012, Chris Andrews. All rights reserved. 3 | */ 4 | 5 | #include "usdt_internal.h" 6 | 7 | #ifdef __APPLE__ 8 | 9 | uint32_t 10 | usdt_probe_offset(usdt_probe_t *probe, char *dof, uint8_t argc) 11 | { 12 | uint32_t offset; 13 | #ifdef __x86_64__ 14 | offset = ((uint64_t) probe->probe_addr - (uint64_t) dof + 2); 15 | #elif __i386__ 16 | offset = ((uint32_t) probe->probe_addr - (uint32_t) dof + 2); 17 | #else 18 | #error "only x86_64 and i386 supported" 19 | #endif 20 | return (offset); 21 | } 22 | 23 | uint32_t 24 | usdt_is_enabled_offset(usdt_probe_t *probe, char *dof) 25 | { 26 | uint32_t offset; 27 | #ifdef __x86_64__ 28 | offset = ((uint64_t) probe->isenabled_addr - (uint64_t) dof + 6); 29 | #elif __i386__ 30 | offset = ((uint32_t) probe->isenabled_addr - (uint32_t) dof + 6); 31 | #else 32 | #error "only x86_64 and i386 supported" 33 | #endif 34 | return (offset); 35 | } 36 | 37 | #elif defined __linux__ 38 | 39 | uint32_t 40 | usdt_probe_offset(usdt_probe_t *probe, char *dof, uint8_t argc) 41 | { 42 | return (16); 43 | } 44 | 45 | uint32_t 46 | usdt_is_enabled_offset(usdt_probe_t *probe, char *dof) 47 | { 48 | return (10); 49 | } 50 | 51 | #else /* solaris and freebsd */ 52 | 53 | uint32_t 54 | usdt_probe_offset(usdt_probe_t *probe, char *dof, uint8_t argc) 55 | { 56 | return (16); 57 | } 58 | 59 | uint32_t 60 | usdt_is_enabled_offset(usdt_probe_t *probe, char *dof) 61 | { 62 | return (8); 63 | } 64 | 65 | #endif 66 | 67 | int 68 | usdt_create_tracepoints(usdt_probe_t *probe) 69 | { 70 | /* Prepare the tracepoints - for each probe, a separate chunk 71 | * of memory with the tracepoint code copied into it, to give 72 | * us unique addresses for each tracepoint. 73 | * 74 | * On Oracle Linux, this must be an mmapped file because USDT 75 | * probes there are implemented as uprobes, which are 76 | * addressed by inode and offset. The file used is a small 77 | * mkstemp'd file we immediately unlink. 78 | * 79 | * Elsewhere, we can use the heap directly because USDT will 80 | * instrument any memory mapped by the process. 81 | */ 82 | 83 | size_t size; 84 | #ifdef __linux__ 85 | int fd; 86 | char tmp[20] = "/tmp/libusdtXXXXXX"; 87 | 88 | if ((fd = mkstemp(tmp)) < 0) 89 | return (-1); 90 | if (unlink(tmp) < 0) 91 | return (-1); 92 | if (write(fd, "\0", FUNC_SIZE) < FUNC_SIZE) 93 | return (-1); 94 | 95 | probe->isenabled_addr = (int (*)())mmap(NULL, FUNC_SIZE, 96 | PROT_READ | PROT_WRITE | PROT_EXEC, 97 | MAP_PRIVATE, fd, 0); 98 | #else 99 | probe->isenabled_addr = (int (*)())valloc(FUNC_SIZE); 100 | #endif 101 | if (probe->isenabled_addr == NULL) 102 | return (-1); 103 | 104 | /* ensure that the tracepoints will fit the heap we're allocating */ 105 | size = ((char *)usdt_tracepoint_end - (char *)usdt_tracepoint_isenabled); 106 | assert(size < FUNC_SIZE); 107 | 108 | size = ((char *)usdt_tracepoint_probe - (char *)usdt_tracepoint_isenabled); 109 | probe->probe_addr = (char *)probe->isenabled_addr + size; 110 | 111 | memcpy((void *)probe->isenabled_addr, 112 | (const void *)usdt_tracepoint_isenabled, FUNC_SIZE); 113 | 114 | #ifdef __linux__ 115 | mprotect((void *)probe->isenabled_addr, FUNC_SIZE, 116 | PROT_READ | PROT_EXEC); 117 | #else 118 | mprotect((void *)probe->isenabled_addr, FUNC_SIZE, 119 | PROT_READ | PROT_WRITE | PROT_EXEC); 120 | #endif 121 | 122 | return (0); 123 | } 124 | 125 | void 126 | usdt_free_tracepoints(usdt_probe_t *probe) 127 | { 128 | #ifdef __linux__ 129 | (void) munmap(probe->isenabled_addr, FUNC_SIZE); 130 | #else 131 | free(probe->isenabled_addr); 132 | #endif 133 | } 134 | -------------------------------------------------------------------------------- /usdt_tracepoints_i386.s: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2012, Chris Andrews. All rights reserved. 3 | */ 4 | 5 | /* 6 | * Stub functions containing DTrace tracepoints for probes and 7 | * is-enabled probes. These functions are copied for each probe 8 | * dynamically created. 9 | * 10 | */ 11 | .text 12 | 13 | .align 4, 0x90 14 | .globl usdt_tracepoint_isenabled 15 | .globl _usdt_tracepoint_isenabled 16 | .globl usdt_tracepoint_probe 17 | .globl _usdt_tracepoint_probe 18 | .globl usdt_tracepoint_end 19 | .globl _usdt_tracepoint_end 20 | .globl usdt_probe_args 21 | .globl _usdt_probe_args 22 | 23 | usdt_tracepoint_isenabled: 24 | _usdt_tracepoint_isenabled: 25 | pushl %ebp 26 | movl %esp, %ebp 27 | subl $8, %esp 28 | xorl %eax, %eax 29 | nop 30 | nop 31 | leave 32 | ret 33 | usdt_tracepoint_probe: 34 | _usdt_tracepoint_probe: 35 | nop 36 | nop 37 | nop 38 | nop 39 | nop 40 | addl $0x20,%esp 41 | leave 42 | usdt_tracepoint_end: 43 | _usdt_tracepoint_end: 44 | ret 45 | 46 | /* 47 | * Probe argument marshalling, i386 style 48 | * 49 | */ 50 | 51 | usdt_probe_args: 52 | _usdt_probe_args: 53 | pushl %ebp 54 | movl %esp,%ebp 55 | subl $8,%esp 56 | subl $8,%esp 57 | movl 8(%ebp),%edx 58 | movl 0xc(%ebp),%ecx 59 | test %ecx,%ecx 60 | je fire 61 | args: movl %ecx,%eax 62 | sal $2,%eax 63 | subl $4,%eax 64 | addl 0x10(%ebp),%eax 65 | pushl (%eax) 66 | dec %ecx 67 | jne args 68 | fire: jmp *%edx 69 | 70 | -------------------------------------------------------------------------------- /usdt_tracepoints_x86_64.s: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2012, Chris Andrews. All rights reserved. 3 | */ 4 | 5 | /* 6 | * Stub functions containing DTrace tracepoints for probes and 7 | * is-enabled probes. These functions are copied for each probe 8 | * dynamically created. 9 | * 10 | */ 11 | .text 12 | 13 | .align 4, 0x90 14 | .globl usdt_tracepoint_isenabled 15 | .globl _usdt_tracepoint_isenabled 16 | .globl usdt_tracepoint_probe 17 | .globl _usdt_tracepoint_probe 18 | .globl usdt_tracepoint_end 19 | .globl _usdt_tracepoint_end 20 | .globl usdt_probe_args 21 | .globl _usdt_probe_args 22 | 23 | usdt_tracepoint_isenabled: 24 | _usdt_tracepoint_isenabled: 25 | pushq %rbp 26 | movq %rsp, %rbp 27 | addq $1, %rax 28 | xorq %rax, %rax 29 | nop 30 | nop 31 | leave 32 | ret 33 | usdt_tracepoint_probe: 34 | _usdt_tracepoint_probe: 35 | nop 36 | nop 37 | nop 38 | nop 39 | nop 40 | addq %r14,%rsp 41 | popq %rbx 42 | popq %r14 43 | popq %r13 44 | popq %r12 45 | leave 46 | usdt_tracepoint_end: 47 | _usdt_tracepoint_end: 48 | ret 49 | 50 | /* 51 | * Probe argument marshalling, x86_64 style 52 | * 53 | */ 54 | 55 | usdt_probe_args: 56 | _usdt_probe_args: 57 | pushq %rbp 58 | movq %rsp,%rbp 59 | pushq %r12 60 | pushq %r13 61 | pushq %r14 62 | pushq %rbx 63 | 64 | movq %rdi,%r12 65 | movq %rsi,%rbx 66 | movq %rdx,%r11 67 | movq $0,%r14 68 | 69 | test %rbx,%rbx 70 | je fire 71 | movq (%r11),%rdi 72 | dec %rbx 73 | test %rbx,%rbx 74 | je fire 75 | addq $8,%r11 76 | movq (%r11),%rsi 77 | dec %rbx 78 | test %rbx,%rbx 79 | je fire 80 | addq $8,%r11 81 | movq (%r11),%rdx 82 | dec %rbx 83 | test %rbx,%rbx 84 | je fire 85 | addq $8,%r11 86 | movq (%r11),%rcx 87 | dec %rbx 88 | test %rbx,%rbx 89 | je fire 90 | addq $8,%r11 91 | movq (%r11),%r8 92 | dec %rbx 93 | test %rbx,%rbx 94 | je fire 95 | addq $8,%r11 96 | movq (%r11),%r9 97 | 98 | movq %rbx,%r13 99 | morestack: 100 | dec %rbx 101 | test %rbx,%rbx 102 | je args 103 | subq $16,%rsp 104 | addq $16,%r14 105 | dec %rbx 106 | test %rbx,%rbx 107 | je args 108 | jmp morestack 109 | 110 | args: 111 | movq %r13,%rbx 112 | movq $0,%r13 113 | moreargs: 114 | dec %rbx 115 | test %rbx,%rbx 116 | je fire 117 | addq $8,%r11 118 | movq (%r11),%rax 119 | movq %rax,(%rsp,%r13) 120 | addq $8,%r13 121 | jmp moreargs 122 | 123 | fire: jmp *%r12 124 | --------------------------------------------------------------------------------