#define uint_t __Solaris_uint_t
#ifdef __cplusplus
extern "C" {
#endif
#include <dt_impl.h>
#include <dt_provider.h>
#include <dt_string.h>
#include <dt_program.h>
#include "dt_dof_byteswap.h"
#include "dt_ld.h"
#ifdef __cplusplus
}
#endif
#include <mach/machine.h>
#include "arch.h"
#include <stdlib.h>
#include <errno.h>
#include <mach/vm_param.h>
#include <string>
#include <vector>
#include <sstream>
#include <unordered_set>
#include <unordered_map>
#include <memory>
#define dtrace_separator "$"
#define dtrace_separator_char '$'
#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 "v2"
#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 std::string dt_ld_encode_string(const char* string)
{
size_t input_length = strlen(string);
std::shared_ptr<char> results((char*)malloc(input_length * 2 + 1), &::free);
for (int i = 0; i < input_length; i++) {
sprintf(&results.get()[i*2],"%02x", (unsigned int)string[i]);
}
results.get()[input_length*2] = 0;
return std::string(results.get());
}
static std::string dt_ld_decode_string(const char* string)
{
size_t input_length = strlen(string) / 2;
std::shared_ptr<char> results((char*)malloc(input_length * 2 + 1), &::free);
for (int i = 0; i < input_length; i++) {
unsigned int value;
sscanf(&string[i*2],"%2x", &value);
results.get()[i] = (unsigned char)value;
}
results.get()[input_length] = 0;
return std::string(results.get());
}
#pragma mark -
#pragma mark stability encoding / decoding
char* dt_ld_encode_stability(char* provider_name, dt_provider_t *provider)
{
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 + 1;
char* buffer = (char*)malloc(bufsize);
snprintf(buffer, bufsize, "%s%s%s%s%s%s%x_%x_%x_%x_%x_%x_%x_%x_%x_%x_%x_%x_%x_%x_%x",
dtrace_stability_encoding_prefix,
dtrace_separator,
provider_name,
dtrace_separator,
dtrace_stability_version,
dtrace_separator,
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,
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,
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,
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,
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;
}
const char* dt_ld_decode_stability_v1_level(int stability_level) {
switch(stability_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!";
};
}
const char* dt_ld_decode_stability_v1_class(int stability_class) {
switch(stability_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!";
};
}
std::string dt_ld_decode_stability_v1(const char* provider, const char* stability)
{
if (strlen(stability) == 29) {
char stability_line_buffer[128];
const char* name = dt_ld_decode_stability_v1_level(stability[0] - '0');
const char* data = dt_ld_decode_stability_v1_level(stability[2] - '0');
const char* stability_class = dt_ld_decode_stability_v1_class(stability[4] - '0');
snprintf(stability_line_buffer, sizeof(stability_line_buffer), "#pragma D attributes %s/%s/%s provider %s provider\n", name, data, stability_class, provider);
std::string decoded_stability(stability_line_buffer);
stability += 6;
name = dt_ld_decode_stability_v1_level(stability[0] - '0');
data = dt_ld_decode_stability_v1_level(stability[2] - '0');
stability_class = dt_ld_decode_stability_v1_class(stability[4] - '0');
snprintf(stability_line_buffer, sizeof(stability_line_buffer), "#pragma D attributes %s/%s/%s provider %s module\n", name, data, stability_class, provider);
decoded_stability += stability_line_buffer;
stability += 6;
name = dt_ld_decode_stability_v1_level(stability[0] - '0');
data = dt_ld_decode_stability_v1_level(stability[2] - '0');
stability_class = dt_ld_decode_stability_v1_class(stability[4] - '0');
snprintf(stability_line_buffer, sizeof(stability_line_buffer), "#pragma D attributes %s/%s/%s provider %s function\n", name, data, stability_class, provider);
decoded_stability += stability_line_buffer;
stability += 6;
name = dt_ld_decode_stability_v1_level(stability[0] - '0');
data = dt_ld_decode_stability_v1_level(stability[2] - '0');
stability_class = dt_ld_decode_stability_v1_class(stability[4] - '0');
snprintf(stability_line_buffer, sizeof(stability_line_buffer), "#pragma D attributes %s/%s/%s provider %s name\n", name, data, stability_class, provider);
decoded_stability += stability_line_buffer;
stability += 6;
name = dt_ld_decode_stability_v1_level(stability[0] - '0');
data = dt_ld_decode_stability_v1_level(stability[2] - '0');
stability_class = dt_ld_decode_stability_v1_class(stability[4] - '0');
snprintf(stability_line_buffer, sizeof(stability_line_buffer), "#pragma D attributes %s/%s/%s provider %s args\n", name, data, stability_class, provider);
return decoded_stability + stability_line_buffer;
}
return "/* Error decoding v1 stability string */";
}
static std::vector<std::string> split(const std::string& s, char delim)
{
std::vector<std::string> elems;
std::stringstream ss(s);
std::string item;
while(std::getline(ss, item, delim)) {
elems.push_back(item);
}
return elems;
}
std::string dt_ld_decode_stability(std::string encoding)
{
std::vector<std::string> elements = split(encoding, dtrace_separator_char);
if (elements.size() == 4) {
if (elements[2] == "v1") {
return dt_ld_decode_stability_v1(elements[1].c_str(), elements[3].c_str());
}
}
return "Unhandled stability encoding version";
}
#pragma mark -
#pragma mark typedef encoding / decoding
typedef std::unordered_set<std::string> ExclusionTypeSet;
static ExclusionTypeSet* base_dtrace_typeset() {
ExclusionTypeSet* typeset = new ExclusionTypeSet();
extern const dt_typedef_t _dtrace_typedefs_32[];
const dt_typedef_t *iter = _dtrace_typedefs_32;
for (; iter->dty_src != NULL; iter++) {
typeset->insert(std::string(iter->dty_dst));
}
extern const dt_typedef_t _dtrace_typedefs_64[];
iter = _dtrace_typedefs_64;
for (; iter->dty_src != NULL; iter++) {
typeset->insert(std::string(iter->dty_dst));
}
return typeset;
}
static bool is_base_dtrace_type(std::string type) {
static ExclusionTypeSet* exclusion_types = base_dtrace_typeset();
return exclusion_types->find(type) != exclusion_types->end();
}
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;
}
typedef std::unordered_map< std::string, std::string > TypeEncodingMap;
static int dt_ld_probe_encode_typedef_iter(dt_idhash_t *dhp, dt_ident_t *idp, void *data)
{
TypeEncodingMap* encoded_types_map = (TypeEncodingMap*)data;
dt_probe_t* probe = (dt_probe_t*)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 = (char*)alloca(size);
ctf_type_lname(node->dn_ctfp, stripped_of_pointers, buf, size);
std::string type_key(buf);
if (!is_base_dtrace_type(type_key)) {
if (encoded_types_map->find(type_key) == encoded_types_map->end()) {
encoded_types_map->insert(TypeEncodingMap::value_type(type_key, dt_ld_encode_string(type_key.c_str())));
}
}
}
}
return 0;
}
char* dt_ld_encode_typedefs(char* provider_name, dt_provider_t *provider)
{
TypeEncodingMap encoded_types_map;
dt_idhash_iter(provider->pv_probes, dt_ld_probe_encode_typedef_iter, (void*)&encoded_types_map);
char encoded_typedefs_line_buffer[256];
snprintf(encoded_typedefs_line_buffer, sizeof(encoded_typedefs_line_buffer), "%s%s%s%s%s",
dtrace_typedefs_encoding_prefix,
dtrace_separator,
provider_name,
dtrace_separator,
dtrace_typedefs_version);
std::string encoded_types(encoded_typedefs_line_buffer);
for (TypeEncodingMap::iterator it = encoded_types_map.begin(); it != encoded_types_map.end(); ++it) {
encoded_types += dtrace_separator;
encoded_types += it->second;
}
if (_dtrace_debug) {
for (TypeEncodingMap::iterator it = encoded_types_map.begin(); it != encoded_types_map.end(); ++it) {
dt_dprintf("dt_ld encoding type %s as %s\n", it->first.c_str(), it->second.c_str());
}
}
return strdup(encoded_types.c_str());
}
std::string dt_ld_decode_typedefs_v1(std::vector<std::string>& typedefs)
{
std::string decoded;
for (size_t i = 3; i < typedefs.size(); i++) {
char line_buffer[128];
snprintf(line_buffer, sizeof(line_buffer), "typedef int %s;\n", dt_ld_decode_string(typedefs[i].c_str()).c_str());
decoded += line_buffer;
}
return decoded;
}
std::string dt_ld_decode_typedefs(std::string encoding, std::string* version_out)
{
std::vector<std::string> elements = split(encoding, dtrace_separator_char);
std::string version;
if (elements.size() >= 3) {
version = elements[2];
if (version_out) {
*version_out = elements[2];
}
}
if (elements.size() >= 4) {
if (version == "v1" || version == "v2") {
return dt_ld_decode_typedefs_v1(elements);
}
return "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)
{
char line_buffer[256];
snprintf(line_buffer, sizeof(line_buffer), "%s%s%s%s%s%s%s",
dtrace_probe_encoding_prefix,
dtrace_separator,
provider_name,
dtrace_separator,
probe_name,
dtrace_separator,
dtrace_probe_version);
std::string encoded(line_buffer);
for (int 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 = (char*)alloca(size);
ctf_type_lname(node->dn_ctfp, node->dn_type, buf, size);
encoded += dtrace_separator_char;
encoded += dt_ld_encode_string(buf);
}
return strdup(encoded.c_str());
}
std::string dt_ld_decode_probe_v1(std::vector<std::string>& arguments)
{
std::string decoded;
for (size_t i = 4; i < arguments.size(); ++i) {
decoded += dt_ld_decode_string(arguments[i].c_str());
if (i+1 < arguments.size()) {
decoded += ',';
}
}
return decoded;
}
std::string dt_ld_decode_probe(std::string encoding)
{
std::vector<std::string> elements = split(encoding, dtrace_separator_char);
if (elements.size() > 3) {
if (elements[3] == "v1") {
std::string probe("\tprobe ");
probe += elements[2];
probe += '(';
if (elements.size() > 4) {
probe += dt_ld_decode_probe_v1(elements);
}
probe += ");";
return probe;
}
}
return "Unhandled probe encoding version";
}
#pragma mark -
#pragma mark isenabled encoding
char* dt_ld_encode_isenabled(char* provider_name, char* probe_name)
{
size_t bufsize = sizeof(dtrace_isenabled_encoding_prefix) +
sizeof(dtrace_separator) +
strlen(provider_name) +
sizeof(dtrace_separator) +
strlen(probe_name) +
1;
char* buffer = (char*)malloc(bufsize);
snprintf(buffer, bufsize, "%s%s%s%s%s%s%s",
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
std::string dt_ld_decode_script(std::string stability, std::string typedefs, std::vector<std::string>& probes)
{
std::string decoded_typedefs = dt_ld_decode_typedefs(typedefs, NULL);
std::string script = dt_ld_decode_typedefs(typedefs, NULL);
script += "\nprovider ";
script += split(stability, dtrace_separator_char)[1]; script += " {\n";
std::unordered_set<std::string> uniqued_probes;
for (std::vector<std::string>::iterator it = probes.begin(); it < probes.end(); ++it) {
std::vector<std::string> components = split(*it, dtrace_separator_char);
if (components.size() < 3) continue;
if (components[0] != dtrace_probe_decoding_prefix)
continue;
std::string probe_name = components[2];
if (uniqued_probes.count(probe_name))
continue;
uniqued_probes.insert(probe_name);
script += dt_ld_decode_probe(*it);
script += '\n';
}
script += "};\n\n";
script += dt_ld_decode_stability(stability);
script += '\n';
return script;
}
#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);
(void) dtrace_setopt(dtp, "nolibs", 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) {
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) {
label += sizeof(dtrace_probe_decoding_prefix) - 1;
is_enabled = 0;
} else {
fprintf(stderr, "error: invalid probe marker: %s\n", label0);
return -1;
}
label += sizeof(dtrace_separator) - 1;
char* end = strstr(label, dtrace_separator);
if(!end) {
fprintf(stderr, "error: probe marker contains no provider name: %s\n", label0);
return -1;
}
char* provider_name = (char*)malloc(end - label + 1);
memcpy(provider_name, label, (end - label));
provider_name[end - label] = 0;
label = end + sizeof(dtrace_separator) - 1;
end = strstr(label, dtrace_separator);
if(!end) {
fprintf(stderr, "error: probe marker contains no probe name: %s\n", label0);
return -1;
}
char* probe_name = (char*)malloc(end - label + 1);
memcpy(probe_name, label, (end - label));
probe_name[end - label] = 0;
probe_name = strhyphenate(probe_name);
dt_provider_t *provider = dt_provider_lookup(dtp, provider_name);
if(!provider) {
fprintf(stderr, "error: provider %s doesn't exist\n", provider_name);
return -1;
}
dt_probe_t* probe = dt_probe_lookup(provider, probe_name);
if(!probe) {
fprintf(stderr, "error: probe %s doesn't exist\n", probe_name);
return -1;
}
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 %s:::%s\n", provider_name, probe_name);
return -1;
}
free(provider_name); free(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:
{
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, unsigned int typeCount, const char* typeNames[], unsigned int probeCount, const char* probeNames[], const char* probeWithin[], uint64_t offsetsInDOF[], size_t* size) {
bool printReconstructedScript = getenv("DTRACE_PRINT_RECONSTRUCTED_SCRIPT") != NULL;
int i, err;
const char* stability = NULL;
const char* typedefs = NULL;
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%s\n%s\n", stability, typeNames[i]);
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) {
std::string existing_info;
std::string new_info;
dt_ld_decode_typedefs(typedefs, &existing_info);
dt_ld_decode_typedefs(typeNames[i], &new_info);
if (existing_info != new_info) {
fprintf(stderr,
"error: Found dtrace typedefs generated by "
"different versions of dtrace:\n%s (%s)\n%s (%s)\n",
typedefs, existing_info.c_str(),
typeNames[i], new_info.c_str());
fprintf(stderr, "Please try regenerating all dtrace created "
"header files with the same version of "
"dtrace before rebuilding your project.\n");
} else {
fprintf(stderr, "error: Found conflicting dtrace typedefs info:\n%s\n%s\n", typedefs, typeNames[i]);
}
return NULL;
}
} else {
fprintf(stderr, "error: Found unhandled dtrace typename prefix: %s\n", typeNames[i]);
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;
}
std::vector<std::string> probes;
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) {
probes.push_back(std::string(probeNames[i]));
} else if (strncmp(probeNames[i], dtrace_isenabled_decoding_prefix, sizeof(dtrace_isenabled_decoding_prefix)-1) == 0) {
probes.push_back(std::string(probeNames[i]));
is_enabled_probes++;
} else {
fprintf(stderr, "error: Found unhandled dtrace probe prefix: %s\n", probeNames[i]);
}
}
if (probes.size() == 0) {
fprintf(stderr, "error: Dtrace provider %s has no probes\n", stability);
return NULL;
}
std::string dscript = dt_ld_decode_script(std::string(stability), std::string(typedefs), probes);
dtrace_hdl_t* dtp = dtrace_open(DTRACE_VERSION,
linker_flags(cpu),
&err);
if(!dtp) {
fprintf(stderr,"error: Failed to initialize dtrace: %s\n", dtrace_errmsg(NULL, err));
return NULL;
}
set_options(dtp);
dtrace_prog_t* program = dtrace_program_strcompile(dtp,
dscript.c_str(),
DTRACE_PROBESPEC_NONE,
0,
0,
NULL);
if(!program) {
fprintf(stderr, "error: Could not compile reconstructed dtrace script:\n\n%s\n", dscript.c_str());
return NULL;
}
if (_dtrace_debug || printReconstructedScript) {
fprintf(stderr, "\n%s\n", dscript.c_str());
}
if(register_probes(dtp, probeCount, probeNames, probeWithin)) {
fprintf(stderr, "error: Could not register probes\n");
return NULL;
}
dof_hdr_t* dof = (dof_hdr_t*)dtrace_dof_create(dtp, program, DTRACE_D_PROBES | DTRACE_D_STRIP);
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);
return return_data;
}