#import <Foundation/Foundation.h>
// This must be done *after* any references to Foundation.h!
#define uint_t __Solaris_uint_t
#include <dt_impl.h>
#include <dt_provider.h>
#include <dt_string.h>
#include <dt_program.h>
#include "dt_dof_byteswap.h"
#include <mach/machine.h>
#include "arch.h"
#include <stdlib.h>
#include <errno.h>
#include <mach/vm_param.h>
#define dtrace_separator "$"
// Why an encoding & decoding prefix? During compilation, the prefix may change...
#define dtrace_stability_encoding_prefix "___dtrace_stability"
#define dtrace_stability_decoding_prefix "___dtrace_stability"
#define dtrace_stability_version "v1"
#define dtrace_typedefs_encoding_prefix "___dtrace_typedefs"
#define dtrace_typedefs_decoding_prefix "___dtrace_typedefs"
#define dtrace_typedefs_version "v1"
#define dtrace_probe_encoding_prefix "__dtrace_probe"
#define dtrace_probe_decoding_prefix "___dtrace_probe"
#define dtrace_probe_version "v1"
#define dtrace_isenabled_encoding_prefix "__dtrace_isenabled"
#define dtrace_isenabled_decoding_prefix "___dtrace_isenabled"
#define dtrace_isenabled_version "v1"
static char* dt_ld_encode_string(char* string)
{
size_t input_length = strlen(string);
char* results = malloc(input_length * 2 + 1);
int i;
for (i=0; i<input_length; i++) {
sprintf(&results[i*2]," }
results[input_length*2] = 0;
return results;
}
static NSString* dt_ld_encode_nsstring(NSString* string)
{
char* results = dt_ld_encode_string((char*)[string UTF8String]);
NSString* value = [NSString stringWithUTF8String:results];
free(results);
return value;
}
static char* dt_ld_decode_string(char* string)
{
size_t input_length = strlen(string) / 2;
unsigned char* results = malloc(input_length + 1);
int i;
for (i=0; i<input_length; i++) {
unsigned int value;
sscanf(&string[i*2]," results[i] = (unsigned char)value;
}
results[input_length] = 0;
return (char*)results;
}
static NSString* dt_ld_decode_nsstring(NSString* string)
{
char* results = dt_ld_decode_string((char*)[string UTF8String]);
NSString* value = [NSString stringWithUTF8String:results];
free(results);
return value;
}
#pragma mark -
#pragma mark stability encoding / decoding
char* dt_ld_encode_stability(char* provider_name, dt_provider_t *provider)
{
// Stability info is encoded as (dtrace_stability_encoding_prefix)(providerName)(dtrace_stability_version)(stability_data)
size_t bufsize = sizeof(dtrace_stability_encoding_prefix) +
sizeof(dtrace_separator) +
sizeof(dtrace_stability_version) +
sizeof(dtrace_separator) +
strlen(provider_name) +
sizeof(dtrace_separator) +
sizeof(dtrace_pattr_t) * 3 + // Each attr is 1 byte * an encoding size of 3 bytes.
1; // NULL terminator
char* buffer = malloc(bufsize);
snprintf(buffer, bufsize, " dtrace_stability_encoding_prefix,
dtrace_separator,
provider_name,
dtrace_separator,
dtrace_stability_version,
dtrace_separator,
/* provider attributes */
provider->pv_desc.dtvd_attr.dtpa_provider.dtat_name,
provider->pv_desc.dtvd_attr.dtpa_provider.dtat_data,
provider->pv_desc.dtvd_attr.dtpa_provider.dtat_class,
/* module attributes */
provider->pv_desc.dtvd_attr.dtpa_mod.dtat_name,
provider->pv_desc.dtvd_attr.dtpa_mod.dtat_data,
provider->pv_desc.dtvd_attr.dtpa_mod.dtat_class,
/* function attributes */
provider->pv_desc.dtvd_attr.dtpa_func.dtat_name,
provider->pv_desc.dtvd_attr.dtpa_func.dtat_data,
provider->pv_desc.dtvd_attr.dtpa_func.dtat_class,
/* name attributes */
provider->pv_desc.dtvd_attr.dtpa_name.dtat_name,
provider->pv_desc.dtvd_attr.dtpa_name.dtat_data,
provider->pv_desc.dtvd_attr.dtpa_name.dtat_class,
/* args[] attributes */
provider->pv_desc.dtvd_attr.dtpa_args.dtat_name,
provider->pv_desc.dtvd_attr.dtpa_args.dtat_data,
provider->pv_desc.dtvd_attr.dtpa_args.dtat_class);
return buffer;
}
char* dt_ld_decode_stability_v1_level(int level) {
switch(level) {
case DTRACE_STABILITY_INTERNAL: return "INTERNAL";
case DTRACE_STABILITY_PRIVATE: return "PRIVATE";
case DTRACE_STABILITY_OBSOLETE: return "OBSOLETE";
case DTRACE_STABILITY_EXTERNAL: return "EXTERNAL";
case DTRACE_STABILITY_UNSTABLE: return "UNSTABLE";
case DTRACE_STABILITY_EVOLVING: return "EVOLVING";
case DTRACE_STABILITY_STABLE: return "STABLE";
case DTRACE_STABILITY_STANDARD: return "STANDARD";
default: return "ERROR!";
};
}
char* dt_ld_decode_stability_v1_class(int class) {
switch(class) {
case DTRACE_CLASS_UNKNOWN: return "UNKNOWN";
case DTRACE_CLASS_CPU: return "CPU";
case DTRACE_CLASS_PLATFORM: return "PLATFORM";
case DTRACE_CLASS_GROUP: return "GROUP";
case DTRACE_CLASS_ISA: return "ISA";
case DTRACE_CLASS_COMMON: return "COMMON";
default: return "ERROR!";
};
}
NSString* dt_ld_decode_stability_v1(NSArray* elements)
{
NSString* provider = [elements objectAtIndex:1];
NSScanner* scanner = [NSScanner scannerWithString:[elements objectAtIndex:3]];
[scanner setCharactersToBeSkipped:[NSCharacterSet characterSetWithCharactersInString:@"_"]];
NSMutableString* stability = [NSMutableString string];
int value;
char* name = dt_ld_decode_stability_v1_level([scanner scanInt:&value] == YES ? value : 255);
char* data = dt_ld_decode_stability_v1_level([scanner scanInt:&value] == YES ? value : 255);
char* class = dt_ld_decode_stability_v1_class([scanner scanInt:&value] == YES ? value : 255);
[stability appendFormat:@"#pragma D attributes
name = dt_ld_decode_stability_v1_level([scanner scanInt:&value] == YES ? value : 255);
data = dt_ld_decode_stability_v1_level([scanner scanInt:&value] == YES ? value : 255);
class = dt_ld_decode_stability_v1_class([scanner scanInt:&value] == YES ? value : 255);
[stability appendFormat:@"#pragma D attributes
name = dt_ld_decode_stability_v1_level([scanner scanInt:&value] == YES ? value : 255);
data = dt_ld_decode_stability_v1_level([scanner scanInt:&value] == YES ? value : 255);
class = dt_ld_decode_stability_v1_class([scanner scanInt:&value] == YES ? value : 255);
[stability appendFormat:@"#pragma D attributes
name = dt_ld_decode_stability_v1_level([scanner scanInt:&value] == YES ? value : 255);
data = dt_ld_decode_stability_v1_level([scanner scanInt:&value] == YES ? value : 255);
class = dt_ld_decode_stability_v1_class([scanner scanInt:&value] == YES ? value : 255);
[stability appendFormat:@"#pragma D attributes
name = dt_ld_decode_stability_v1_level([scanner scanInt:&value] == YES ? value : 255);
data = dt_ld_decode_stability_v1_level([scanner scanInt:&value] == YES ? value : 255);
class = dt_ld_decode_stability_v1_class([scanner scanInt:&value] == YES ? value : 255);
[stability appendFormat:@"#pragma D attributes
return stability;
}
NSString* dt_ld_decode_stability(NSString* encoding)
{
NSArray* elements = [encoding componentsSeparatedByString:@dtrace_separator];
NSString* version = [elements objectAtIndex:2];
if ([version isEqualToString:@"v1"])
return dt_ld_decode_stability_v1(elements);
// Wow, no good way to handle error conditions here.
return [NSString stringWithFormat:@"Unhandled stability encoding version }
#pragma mark -
#pragma mark typedef encoding / decoding
//
// If the input type is a pointer, return the type pointed to.
// This method is recursive, it will walk back a chain of pointers
// until it reaches a non pointer type.
//
// If the original type is not a pointer, it is returned unchanged.
static ctf_id_t dt_ld_strip_pointers(ctf_file_t *file, ctf_id_t type) {
if (ctf_type_kind(file, type) == CTF_K_POINTER) {
return dt_ld_strip_pointers(file, ctf_type_reference(file, type));
}
return type;
}
// This method requires the caller have a valid NSAutoreleasePool
//
// This method works as follows:
//
// 1) Strip any pointer'dness from the arg type
// 2) If the resulting type != a base type, assume it needs a typedef
// 3) We *DO NOT* retain the original type. Everything that is typedef'd is forced to int, I.E.
//
// original: typedef float*** foo_t
// encoded: typedef int foo_t
static int dt_ld_probe_encode_typedef_iter(dt_idhash_t *dhp, dt_ident_t *idp, void *data)
{
NSMutableDictionary* types = data;
dt_probe_t *probe = idp->di_data;
dt_node_t* node;
for (node = probe->pr_nargs; node != NULL; node = node->dn_list) {
ctf_id_t stripped_of_pointers = dt_ld_strip_pointers(node->dn_ctfp, node->dn_type);
ctf_id_t base = ctf_type_resolve(node->dn_ctfp, stripped_of_pointers);
if (base != stripped_of_pointers) {
ssize_t size = ctf_type_lname(node->dn_ctfp, stripped_of_pointers, NULL, 0) + 1;
char* buf = alloca(size);
ctf_type_lname(node->dn_ctfp, stripped_of_pointers, buf, size);
NSString* typeKey = [NSString stringWithUTF8String:buf];
if ([types objectForKey:typeKey] == nil) {
[types setObject:dt_ld_encode_nsstring(typeKey) forKey:typeKey];
}
}
}
return 0;
}
char* dt_ld_encode_typedefs(char* provider_name, dt_provider_t *provider)
{
NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
NSMutableDictionary* types = [NSMutableDictionary dictionary];
dt_idhash_iter(provider->pv_probes, dt_ld_probe_encode_typedef_iter, types);
NSArray* values = [types allValues];
NSUInteger i, count = [values count];
NSMutableString* string = [NSMutableString stringWithFormat:@" dtrace_typedefs_encoding_prefix,
dtrace_separator,
provider_name,
dtrace_separator,
dtrace_typedefs_version,
nil];
for (i=0; i<count; i++) {
[string appendFormat:@" }
char* value = strdup([string UTF8String]);
[pool drain];
return value;
}
NSString* dt_ld_decode_typedefs_v1(NSArray* typedefs)
{
NSMutableString* decoded = [NSMutableString string];
NSUInteger i, count = [typedefs count];
for (i=3; i<count; i++) {
[decoded appendFormat:@"typedef int }
return decoded;
}
NSString* dt_ld_decode_typedefs(NSString* encoding)
{
NSArray* elements = [encoding componentsSeparatedByString:@dtrace_separator];
// Is anything actually encoded?
if ([elements count] > 3) {
NSString* version = [elements objectAtIndex:2];
if ([version isEqualToString:@"v1"])
return dt_ld_decode_typedefs_v1(elements);
else {
// Wow, no good way to handle error conditions here.
return [NSString stringWithFormat:@"Unhandled typedefs encoding version }
}
return @"";
}
#pragma mark -
#pragma mark probe encoding / decoding
char* dt_ld_encode_probe(char* provider_name, char* probe_name, dt_probe_t* probe)
{
NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
NSMutableString* string = [NSMutableString stringWithFormat:@" dtrace_probe_encoding_prefix,
dtrace_separator,
provider_name,
dtrace_separator,
probe_name,
dtrace_separator,
dtrace_probe_version,
nil];
int i;
for (i=0; i<probe->pr_nargc; i++) {
dt_node_t* node = probe->pr_nargv[i];
ssize_t size = ctf_type_lname(node->dn_ctfp, node->dn_type, NULL, 0) + 1;
char* buf = alloca(size);
ctf_type_lname(node->dn_ctfp, node->dn_type, buf, size);
char* encoded_buf = dt_ld_encode_string(buf);
[string appendFormat:@" }
char* value = strdup([string UTF8String]);
[pool drain];
return value;
}
NSString* dt_ld_decode_probe_v1(NSArray* arguments)
{
NSMutableString* decoded = [NSMutableString string];
NSUInteger i, count = [arguments count];
for (i=4; i<count; i++) {
if (i+1 < count)
[decoded appendFormat:@" else
[decoded appendFormat:@" }
return decoded;
}
NSString* dt_ld_decode_probe(NSString* encoding)
{
NSArray* elements = [encoding componentsSeparatedByString:@dtrace_separator];
NSMutableString* probe = [NSMutableString stringWithFormat:@"\tprobe
if ([elements count] > 4) {
NSString* version = [elements objectAtIndex:3];
if ([version isEqualToString:@"v1"])
[probe appendFormat:@" else {
// Wow, no good way to handle error conditions here.
[probe appendFormat:@"Unhandled probe encoding version }
}
[probe appendFormat:@");"];
return probe;
}
#pragma mark -
#pragma mark isenabled encoding
char* dt_ld_encode_isenabled(char* provider_name, char* probe_name)
{
// "isenabled" probe info is encoded as (dtrace_isenabled_encoding_prefix)(providerName)(probe_name)
size_t bufsize = sizeof(dtrace_isenabled_encoding_prefix) +
sizeof(dtrace_separator) +
strlen(provider_name) +
sizeof(dtrace_separator) +
strlen(probe_name) +
1; // NULL terminator
char* buffer = malloc(bufsize);
snprintf(buffer, bufsize, " dtrace_isenabled_encoding_prefix,
dtrace_separator,
provider_name,
dtrace_separator,
probe_name,
dtrace_separator,
dtrace_isenabled_version);
return buffer;
}
#pragma mark -
#pragma mark D Script regeneration
NSString* dt_ld_decode_script(NSString* stability, NSString* typedefs, NSArray* probes)
{
NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
NSString* decodedTypedefs = dt_ld_decode_typedefs(typedefs);
NSMutableString* script = [[NSMutableString alloc] initWithFormat:@"
// Maybe we should pass the provider name in? Do some error checking?
NSString* provider = [[stability componentsSeparatedByString:@dtrace_separator] objectAtIndex:1];
[script appendFormat:@"provider
NSMutableDictionary* uniquedProbes = [NSMutableDictionary dictionary];
for (NSString* probe in probes) {
NSArray* components = [probe componentsSeparatedByString:@dtrace_separator];
if ([components count] < 3) // Can't be a probe spec
continue;
if (![[components objectAtIndex:0] isEqualToString:@dtrace_probe_decoding_prefix])
continue;
NSString* probeName = [components objectAtIndex:2];
if ([uniquedProbes objectForKey:probeName] != nil)
continue;
[uniquedProbes setObject:probeName forKey:probeName];
[script appendFormat:@" }
[script appendFormat:@"};\n\n", provider];
[script appendFormat:@"
[pool drain];
return [script autorelease];
}
#pragma mark -
#pragma mark Linker support
static int linker_flags(cpu_type_t cpu)
{
int oflags = 0;
oflags |= DTRACE_O_NODEV;
if(cpu & CPU_ARCH_ABI64)
oflags |= DTRACE_O_LP64;
else
oflags |= DTRACE_O_ILP32;
return oflags;
}
static void set_options(dtrace_hdl_t* dtp)
{
(void) dtrace_setopt(dtp, "linkmode", "dynamic");
(void) dtrace_setopt(dtp, "unodefs", NULL);
}
static int register_probes(dtrace_hdl_t* dtp, int count, const char* labels[], const char* functions[])
{
int i;
int is_enabled = 0;
for(i = 0; i < count; i++) {
const char* label = labels[i];
const char* label0 = label;
if(strncmp(label, dtrace_isenabled_decoding_prefix, sizeof(dtrace_isenabled_decoding_prefix) - 1) == 0) {
// skip prefix
label += sizeof(dtrace_isenabled_decoding_prefix) - 1;
is_enabled = 1;
} else if (strncmp(label, dtrace_probe_decoding_prefix, sizeof(dtrace_probe_decoding_prefix) - 1) == 0) {
// skip prefix
label += sizeof(dtrace_probe_decoding_prefix) - 1;
is_enabled = 0;
} else {
fprintf(stderr, "error: invalid probe marker: return -1;
}
// skip separator
label += sizeof(dtrace_separator) - 1;
// Grab the provider name
char* end = strstr(label, dtrace_separator);
if(!end) {
fprintf(stderr, "error: probe marker contains no provider name: return -1;
}
char* provider_name = malloc(end - label + 1);
memcpy(provider_name, label, (end - label));
provider_name[end - label] = 0;
// Skip the separator
label = end + sizeof(dtrace_separator) - 1;
// Grab the probe name
end = strstr(label, dtrace_separator);
if(!end) {
fprintf(stderr, "error: probe marker contains no probe name: return -1;
}
char* probe_name = malloc(end - label + 1);
memcpy(probe_name, label, (end - label));
probe_name[end - label] = 0;
probe_name = strhyphenate(probe_name);
// now, register the probe
dt_provider_t *provider = dt_provider_lookup(dtp, provider_name);
if(!provider) {
fprintf(stderr, "error: provider return -1;
}
dt_probe_t* probe = dt_probe_lookup(provider, probe_name);
if(!probe) {
fprintf(stderr, "error: probe return -1;
}
// The "raw" function names provided by the linker will have an underscore
// prepended. Remove it before registering the probe function.
const char* function_name = functions[i];
if (function_name[0] == '_')
function_name++;
if(dt_probe_define(provider, probe, function_name, NULL, i, is_enabled)) {
fprintf(stderr, "error: couldn't define probe return -1;
}
free(provider_name); // free() of provider_name
free(probe_name); // free() of probe_name
}
return 0;
}
int register_offsets(dof_hdr_t* header, int count, uint64_t offsetsInDOF[])
{
dof_sec_t* sections = (dof_sec_t*)((char*)header + header->dofh_secoff);
int i;
for(i = 0; i < count; i++)
offsetsInDOF[i] = (uint64_t)-1;
for(i = 0; i < header->dofh_secnum; i++) {
switch(sections[i].dofs_type) {
case DOF_SECT_PROFFS:
case DOF_SECT_PRENOFFS:
{
// As each probe is defined, it gets a uint32_t entry that indicates its offset.
// In the Sun DOF, this is the offset from the start of the function the probe
// resides in. We use it to mean "offset to this probe from the start of the
// DOF section". We stored the relocation_index as a placeholder in the
// register_probes() function.
uint32_t* probe_offsets = (uint32_t*)((char*)header + sections[i].dofs_offset);
uint32_t j, count = sections[i].dofs_size / sizeof(uint32_t);
for (j=0; j<count; j++) {
int relocation_index = probe_offsets[j];
offsetsInDOF[relocation_index] = (uint64_t)(unsigned long)((char*)&probe_offsets[j] - (char*)header);
}
}
break;
}
}
return 0;
}
void* dtrace_ld_create_dof(cpu_type_t cpu, // [provided by linker] target architecture
unsigned int typeCount, // [provided by linker] number of stability or typedef symbol names
const char* typeNames[], // [provided by linker] stability or typedef symbol names
unsigned int probeCount, // [provided by linker] number of probe or isenabled locations
const char* probeNames[], // [provided by linker] probe or isenabled symbol names
const char* probeWithin[], // [provided by linker] function name containing probe or isenabled
uint64_t offsetsInDOF[], // [allocated by linker, populated by DTrace] per-probe offset in the DOF
size_t* size) // [allocated by linker, populated by DTrace] size of the DOF)
{
NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
int i, err;
const char* stability = NULL;
const char* typedefs = NULL;
// First, find a valid stability and typedefs.
for (i=0; i<typeCount; i++) {
if (strncmp(typeNames[i], dtrace_stability_decoding_prefix, sizeof(dtrace_stability_decoding_prefix)-1) == 0) {
if (stability == NULL) {
stability = typeNames[i];
} else if (strcmp(stability, typeNames[i]) != 0) {
fprintf(stderr, "error: Found conflicting dtrace stability info:\n return NULL;
}
} else if (strncmp(typeNames[i], dtrace_typedefs_decoding_prefix, sizeof(dtrace_typedefs_decoding_prefix)-1) == 0) {
if (typedefs == NULL) {
typedefs = typeNames[i];
} else if (strcmp(typedefs, typeNames[i]) != 0) {
fprintf(stderr, "error: Found conflicting dtrace typedefs info:\n return NULL;
}
} else {
fprintf(stderr, "error: Found unhandled dtrace typename prefix: return NULL;
}
}
if (stability == NULL) {
fprintf(stderr, "error: Must have a valid dtrace stability entry\n");
return NULL;
}
if (typedefs == NULL) {
fprintf(stderr, "error: Must have a a valid dtrace typedefs entry\n");
return NULL;
}
// Recreate the provider.d script
NSMutableArray* probes = [NSMutableArray arrayWithCapacity:probeCount];
int is_enabled_probes = 0;
for (i=0; i<probeCount; i++) {
if (strncmp(probeNames[i], dtrace_probe_decoding_prefix, sizeof(dtrace_probe_decoding_prefix)-1) == 0) {
// Assert this belongs to the correct provider!
[probes addObject:[NSString stringWithUTF8String:probeNames[i]]];
} else if (strncmp(probeNames[i], dtrace_isenabled_decoding_prefix, sizeof(dtrace_isenabled_decoding_prefix)-1) == 0) {
// Assert this belongs to the correct provider!
[probes addObject:[NSString stringWithUTF8String:probeNames[i]]];
is_enabled_probes++;
} else {
fprintf(stderr, "error: Found unhandled dtrace probe prefix: }
}
if ([probes count] == 0) {
fprintf(stderr, "error: Dtrace provider return NULL;
}
NSString* dscript = dt_ld_decode_script([NSString stringWithUTF8String:stability],
[NSString stringWithUTF8String:typedefs],
probes);
dtrace_hdl_t* dtp = dtrace_open(DTRACE_VERSION,
linker_flags(cpu),
&err);
if(!dtp) {
fprintf(stderr,"error: Failed to initialize dtrace: return NULL;
}
set_options(dtp);
dtrace_prog_t* program = dtrace_program_strcompile(dtp,
[dscript UTF8String],
DTRACE_PROBESPEC_NONE,
0,
0,
NULL);
if(!program) {
fprintf(stderr, "error: Could not compile reconstructed dtrace script:\n\n return NULL;
}
if(register_probes(dtp, probeCount, probeNames, probeWithin)) {
fprintf(stderr, "error: Could not register probes\n");
return NULL;
}
dof_hdr_t* dof = dtrace_dof_create(dtp, program, DTRACE_D_PROBES);
if(register_offsets(dof, probeCount, offsetsInDOF)) {
fprintf(stderr, "error: Could not register DOF offsets\n");
return NULL;
}
*size = dof->dofh_filesz;
void* return_data = malloc(*size);
memcpy(return_data, dof, *size);
if(needs_swapping(host_arch, cpu))
dtrace_dof_byteswap((dof_hdr_t*)return_data);
dtrace_dof_destroy(dtp, dof);
dtrace_close(dtp);
[pool drain];
return return_data;
}