Skip to content

Replace atos backtrace generation with a dSYM loader. #4930

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 10 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
144 changes: 2 additions & 142 deletions runtime/druntime/src/core/internal/backtrace/dwarf.d
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@
* Reference: http://www.dwarfstd.org/
* Copyright: Copyright Digital Mars 2015 - 2015.
* License: $(HTTP www.boost.org/LICENSE_1_0.txt, Boost License 1.0).
* Authors: Yazan Dabain, Sean Kelly
* Authors: Yazan Dabain, Sean Kelly, Luna Nielsen
* Source: $(DRUNTIMESRC core/internal/backtrace/dwarf.d)
*/

Expand All @@ -69,6 +69,7 @@ else

import core.internal.container.array;
import core.stdc.string : strlen, memcpy;
import core.attribute : weak;

//debug = DwarfDebugMachine;
debug(DwarfDebugMachine) import core.stdc.stdio : printf;
Expand Down Expand Up @@ -184,7 +185,6 @@ int traceHandlerOpApplyImpl(size_t numFrames,
startIdx = idx + 1;
}


if (!image.isValid())
return locations[startIdx .. $].processCallstack(null, 0, dg);

Expand Down Expand Up @@ -244,11 +244,6 @@ int processCallstack(Location[] locations, const(ubyte)[] debugLineSectionData,
{
if (debugLineSectionData)
resolveAddresses(debugLineSectionData, locations, baseAddress);
version (Darwin)
{
if (!debugLineSectionData)
resolveAddressesWithAtos(locations);
}

TraceInfoBuffer buffer;
foreach (idx, const ref loc; locations)
Expand All @@ -267,141 +262,6 @@ int processCallstack(Location[] locations, const(ubyte)[] debugLineSectionData,
return 0;
}

version (Darwin) {
/**
* Resolve the addresses of `locations` using `atos` (executable that ships with XCode)
*
* Spawns a child process that calls `atos`. Communication is through stdin/stdout pipes.
*
* After this function successfully completes, `locations` will contain
* file / lines informations.
*
* The lifetime of the `Location` data surpases function return (strndup is used).
*
* Params:
* locations = The locations to resolve
*/
private void resolveAddressesWithAtos(Location[] locations) @nogc nothrow
{
import core.stdc.stdio : fclose, fflush, fgets, fprintf, printf, snprintf;
import core.stdc.stdlib : exit;
import core.sys.posix.stdio : fdopen;
import core.sys.posix.unistd : close, dup2, execlp, fork, getpid, pipe;
// Create in/out pipes to communicate with the forked exec
int[2] dummy_pipes; // these dummy pipes are there to prevent funny issues when stdin/stdout is closed and pipe returns id 0 or 1
int[2] pipes_to_atos;
int[2] pipes_from_atos;
if ( pipe(dummy_pipes) < 0 || pipe(pipes_to_atos) < 0 || pipe(pipes_from_atos) < 0 ) {
printf("some pipe creation error!\n");
return;
}
close(dummy_pipes[0]);
close(dummy_pipes[1]);
auto write_to_atos = pipes_to_atos[1];
auto read_from_atos = pipes_from_atos[0];
auto atos_stdin = pipes_to_atos[0];
auto atos_stdout = pipes_from_atos[1];
auto self_pid = cast(int) getpid();
// Spawn a child process that calls atos, reads/writes from the pipes, and then exits.
auto child_id = fork();
if (child_id == -1)
{
printf("some fork error!\n");
return;
}
else if (child_id == 0)
{
// We are in the child process, spawn atos and link pipes
// Close unused read/write ends of pipes
close(write_to_atos);
close(read_from_atos);
// Link pipes to stdin/stdout
dup2(atos_stdin, 0);
close(atos_stdin);
dup2(atos_stdout, 1);
close(atos_stdout);
char[10] pid_str;
snprintf(pid_str.ptr, pid_str.sizeof, "%d", cast(int) self_pid);
const(char)* atos_executable = "atos";
const(char)* atos_p_arg = "-p";
const(char)* atos_fullpath_arg = "-fullPath";
execlp(atos_executable, atos_executable, atos_fullpath_arg, atos_p_arg, pid_str.ptr, null);
// If exec returns, an error occurred, need to exit the forked process here.
printf("some exec error!\n");
exit(0);
}
// Parent process just continues from here.
// Close unused pipes
close(atos_stdin);
close(atos_stdout);
auto to_atos = fdopen(write_to_atos, "w");
auto from_atos = fdopen(read_from_atos, "r");
// buffer for atos reading. Note that symbol names can be super large...
static char[16 * 1024] read_buffer = void;
char* status_ptr = null;
foreach (ref loc; locations)
{
fprintf(to_atos, "%p\n", loc.address);
fflush(to_atos);
read_buffer[0] = '\0';
status_ptr = fgets(read_buffer.ptr, read_buffer.sizeof, from_atos);
if (!status_ptr)
break;
Location parsed_loc = parseAtosLine(read_buffer.ptr);
if (parsed_loc.line != -1)
{
// Only update the file:line info, keep the procedure name as found before (preserving the standard truncation).
loc.file = parsed_loc.file;
loc.line = parsed_loc.line;
}
}
if (!status_ptr)
printf("\nDid not succeed in using 'atos' for extra debug information.\n");
fclose(to_atos);
fclose(from_atos);
close(write_to_atos);
close(read_from_atos);
}
private Location parseAtosLine(char* buffer) @nogc nothrow
{
// The line from `atos` is in one of these formats:
// myfunction (in library.dylib) (sourcefile.c:17)
// myfunction (in library.dylib) + 0x1fe
// myfunction (in library.dylib) + 15
// 0xdeadbeef (in library.dylib) + 0x1fe
// 0xdeadbeef (in library.dylib) + 15
// 0xdeadbeef (in library.dylib)
// 0xdeadbeef
import core.stdc.stdlib : atoi;
import core.stdc.string : strchr, strstr;
import core.sys.posix.string : strndup;
Location loc;
if (!buffer)
return loc;
if (buffer[0] == '0' && buffer[1] == 'x')
// no named symbol found
return loc;
const symbolname_end = strstr(buffer, " (in ");
if (!symbolname_end)
return loc;
const symbolname_size = symbolname_end - buffer;
loc.procedure = strndup(buffer, symbolname_size)[0..symbolname_size];
const filename_start = strstr(symbolname_end, ") (") + 3;
if (cast(size_t)filename_start < 4)
return loc;
const colon_location = strchr(filename_start, ':');
if (!colon_location)
return loc;
const filename_size = colon_location - filename_start;
loc.file = strndup(filename_start, filename_size)[0..filename_size];
const final_paren = strchr(colon_location+1, ')');
if (!final_paren)
return loc;
loc.line = atoi(colon_location+1);
return loc;
}
}

/**
* Resolve the addresses of `locations` using `debugLineSectionData`
*
Expand Down
45 changes: 26 additions & 19 deletions runtime/druntime/src/core/internal/backtrace/macho.d
Original file line number Diff line number Diff line change
Expand Up @@ -20,32 +20,39 @@ else version (WatchOS)
version (Darwin):

import core.stdc.config : c_ulong;
import core.sys.darwin.crt_externs : _NSGetMachExecuteHeader;
import core.sys.darwin.mach.getsect : mach_header_64, getsectiondata;

struct Image
{
private mach_header_64* self;
import core.stdc.stdlib : free;
import core.internal.macho.dl;
import core.internal.macho.io;

struct Image {
SharedObject self;
SharedObject debugObj;

this(SharedObject self) {
this.self = self;
this.debugObj = self;

if (!self.hasSection("__DWARF", "__debug_line")) {
auto dsymPath = getDsymDefaultPath();
this.debugObj = SharedObject.fromFile(dsymPath.ptr);
}
}

static Image openSelf()
{
return Image(_NSGetMachExecuteHeader());
T processDebugLineSectionData(T)(scope T delegate(const(ubyte)[]) processor) {
return processor(debugObj.getSection("__DWARF", "__debug_line"));
}

@property bool isValid()
{
return self !is null;
static Image openSelf() {
return Image(SharedObject.thisExecutable());
}

T processDebugLineSectionData(T)(scope T delegate(const(ubyte)[]) processor)
{
c_ulong size;
auto data = getsectiondata(self, "__DWARF", "__debug_line", &size);
return processor(data[0 .. size]);
@property bool isValid() {
return self.isValid;
}

@property size_t baseAddress()
{
@property size_t baseAddress() {
if (debugObj.slide == -1)
return self.slide;
return 0;
}
}
Loading