[Sanitizer] External symbolizer refactoring: split protocol for communicating with
llvm-symbolizer binary and external process handling into separate classes. No functionality change. llvm-svn: 201150
This commit is contained in:
parent
9d80849714
commit
ca183eed55
|
@ -54,24 +54,136 @@ static const char *DemangleCXXABI(const char *name) {
|
|||
return name;
|
||||
}
|
||||
|
||||
#if defined(__x86_64__)
|
||||
static const char* const kSymbolizerArch = "--default-arch=x86_64";
|
||||
#elif defined(__i386__)
|
||||
static const char* const kSymbolizerArch = "--default-arch=i386";
|
||||
#elif defined(__powerpc64__)
|
||||
static const char* const kSymbolizerArch = "--default-arch=powerpc64";
|
||||
#else
|
||||
static const char* const kSymbolizerArch = "--default-arch=unknown";
|
||||
#endif
|
||||
// Extracts the prefix of "str" that consists of any characters not
|
||||
// present in "delims" string, and copies this prefix to "result", allocating
|
||||
// space for it.
|
||||
// Returns a pointer to "str" after skipping extracted prefix and first
|
||||
// delimiter char.
|
||||
static const char *ExtractToken(const char *str, const char *delims,
|
||||
char **result) {
|
||||
uptr prefix_len = internal_strcspn(str, delims);
|
||||
*result = (char*)InternalAlloc(prefix_len + 1);
|
||||
internal_memcpy(*result, str, prefix_len);
|
||||
(*result)[prefix_len] = '\0';
|
||||
const char *prefix_end = str + prefix_len;
|
||||
if (*prefix_end != '\0') prefix_end++;
|
||||
return prefix_end;
|
||||
}
|
||||
|
||||
static const int kSymbolizerStartupTimeMillis = 10;
|
||||
// Same as ExtractToken, but converts extracted token to integer.
|
||||
static const char *ExtractInt(const char *str, const char *delims,
|
||||
int *result) {
|
||||
char *buff;
|
||||
const char *ret = ExtractToken(str, delims, &buff);
|
||||
if (buff != 0) {
|
||||
*result = (int)internal_atoll(buff);
|
||||
}
|
||||
InternalFree(buff);
|
||||
return ret;
|
||||
}
|
||||
|
||||
// Creates external symbolizer connected via pipe, user should write
|
||||
// to output_fd and read from input_fd.
|
||||
static bool StartSymbolizerSubprocess(const char *path_to_symbolizer,
|
||||
int *input_fd, int *output_fd) {
|
||||
if (!FileExists(path_to_symbolizer)) {
|
||||
static const char *ExtractUptr(const char *str, const char *delims,
|
||||
uptr *result) {
|
||||
char *buff;
|
||||
const char *ret = ExtractToken(str, delims, &buff);
|
||||
if (buff != 0) {
|
||||
*result = (uptr)internal_atoll(buff);
|
||||
}
|
||||
InternalFree(buff);
|
||||
return ret;
|
||||
}
|
||||
|
||||
// SymbolizerProcess encapsulates communication between the tool and
|
||||
// external symbolizer program, running in a different subprocess.
|
||||
// SymbolizerProcess may not be used from two threads simultaneously.
|
||||
class SymbolizerProcess {
|
||||
public:
|
||||
explicit SymbolizerProcess(const char *path)
|
||||
: path_(path),
|
||||
input_fd_(kInvalidFd),
|
||||
output_fd_(kInvalidFd),
|
||||
times_restarted_(0),
|
||||
failed_to_start_(false),
|
||||
reported_invalid_path_(false) {
|
||||
CHECK(path_);
|
||||
CHECK_NE(path_[0], '\0');
|
||||
}
|
||||
|
||||
char *SendCommand(bool is_data, const char *module_name, uptr module_offset) {
|
||||
for (; times_restarted_ < kMaxTimesRestarted; times_restarted_++) {
|
||||
// Start or restart symbolizer if we failed to send command to it.
|
||||
if (char *res = SendCommandImpl(is_data, module_name, module_offset))
|
||||
return res;
|
||||
Restart();
|
||||
}
|
||||
if (!failed_to_start_) {
|
||||
Report("WARNING: Failed to use and restart external symbolizer!\n");
|
||||
failed_to_start_ = true;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
private:
|
||||
bool Restart() {
|
||||
if (input_fd_ != kInvalidFd)
|
||||
internal_close(input_fd_);
|
||||
if (output_fd_ != kInvalidFd)
|
||||
internal_close(output_fd_);
|
||||
return StartSymbolizerSubprocess();
|
||||
}
|
||||
|
||||
char *SendCommandImpl(bool is_data, const char *module_name,
|
||||
uptr module_offset) {
|
||||
if (input_fd_ == kInvalidFd || output_fd_ == kInvalidFd)
|
||||
return 0;
|
||||
CHECK(module_name);
|
||||
if (!RenderInputCommand(buffer_, kBufferSize, is_data, module_name,
|
||||
module_offset))
|
||||
return 0;
|
||||
if (!writeToSymbolizer(buffer_, internal_strlen(buffer_)))
|
||||
return 0;
|
||||
if (!readFromSymbolizer(buffer_, kBufferSize))
|
||||
return 0;
|
||||
return buffer_;
|
||||
}
|
||||
|
||||
bool readFromSymbolizer(char *buffer, uptr max_length) {
|
||||
if (max_length == 0)
|
||||
return true;
|
||||
uptr read_len = 0;
|
||||
while (true) {
|
||||
uptr just_read = internal_read(input_fd_, buffer + read_len,
|
||||
max_length - read_len);
|
||||
// We can't read 0 bytes, as we don't expect external symbolizer to close
|
||||
// its stdout.
|
||||
if (just_read == 0 || just_read == (uptr)-1) {
|
||||
Report("WARNING: Can't read from symbolizer at fd %d\n", input_fd_);
|
||||
return false;
|
||||
}
|
||||
read_len += just_read;
|
||||
if (ReachedEndOfOutput(buffer, read_len))
|
||||
break;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool writeToSymbolizer(const char *buffer, uptr length) {
|
||||
if (length == 0)
|
||||
return true;
|
||||
uptr write_len = internal_write(output_fd_, buffer, length);
|
||||
if (write_len == 0 || write_len == (uptr)-1) {
|
||||
Report("WARNING: Can't write to symbolizer at fd %d\n", output_fd_);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool StartSymbolizerSubprocess() {
|
||||
if (!FileExists(path_)) {
|
||||
if (!reported_invalid_path_) {
|
||||
Report("WARNING: invalid path to external symbolizer!\n");
|
||||
reported_invalid_path_ = true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -132,15 +244,15 @@ static bool StartSymbolizerSubprocess(const char *path_to_symbolizer,
|
|||
internal_close(infd[1]);
|
||||
for (int fd = getdtablesize(); fd > 2; fd--)
|
||||
internal_close(fd);
|
||||
execl(path_to_symbolizer, path_to_symbolizer, kSymbolizerArch, (char*)0);
|
||||
ExecuteWithDefaultArgs(path_);
|
||||
internal__exit(1);
|
||||
}
|
||||
|
||||
// Continue execution in parent process.
|
||||
internal_close(outfd[0]);
|
||||
internal_close(infd[1]);
|
||||
*input_fd = infd[0];
|
||||
*output_fd = outfd[1];
|
||||
input_fd_ = infd[0];
|
||||
output_fd_ = outfd[1];
|
||||
|
||||
// Check that symbolizer subprocess started successfully.
|
||||
int pid_status;
|
||||
|
@ -155,139 +267,18 @@ static bool StartSymbolizerSubprocess(const char *path_to_symbolizer,
|
|||
return true;
|
||||
}
|
||||
|
||||
// Extracts the prefix of "str" that consists of any characters not
|
||||
// present in "delims" string, and copies this prefix to "result", allocating
|
||||
// space for it.
|
||||
// Returns a pointer to "str" after skipping extracted prefix and first
|
||||
// delimiter char.
|
||||
static const char *ExtractToken(const char *str, const char *delims,
|
||||
char **result) {
|
||||
uptr prefix_len = internal_strcspn(str, delims);
|
||||
*result = (char*)InternalAlloc(prefix_len + 1);
|
||||
internal_memcpy(*result, str, prefix_len);
|
||||
(*result)[prefix_len] = '\0';
|
||||
const char *prefix_end = str + prefix_len;
|
||||
if (*prefix_end != '\0') prefix_end++;
|
||||
return prefix_end;
|
||||
virtual bool RenderInputCommand(char *buffer, uptr max_length, bool is_data,
|
||||
const char *module_name,
|
||||
uptr module_offset) const {
|
||||
UNIMPLEMENTED();
|
||||
}
|
||||
|
||||
// Same as ExtractToken, but converts extracted token to integer.
|
||||
static const char *ExtractInt(const char *str, const char *delims,
|
||||
int *result) {
|
||||
char *buff;
|
||||
const char *ret = ExtractToken(str, delims, &buff);
|
||||
if (buff != 0) {
|
||||
*result = (int)internal_atoll(buff);
|
||||
}
|
||||
InternalFree(buff);
|
||||
return ret;
|
||||
virtual bool ReachedEndOfOutput(const char *buffer, uptr length) const {
|
||||
UNIMPLEMENTED();
|
||||
}
|
||||
|
||||
static const char *ExtractUptr(const char *str, const char *delims,
|
||||
uptr *result) {
|
||||
char *buff;
|
||||
const char *ret = ExtractToken(str, delims, &buff);
|
||||
if (buff != 0) {
|
||||
*result = (uptr)internal_atoll(buff);
|
||||
}
|
||||
InternalFree(buff);
|
||||
return ret;
|
||||
}
|
||||
|
||||
// ExternalSymbolizer encapsulates communication between the tool and
|
||||
// external symbolizer program, running in a different subprocess,
|
||||
// For now we assume the following protocol:
|
||||
// For each request of the form
|
||||
// <module_name> <module_offset>
|
||||
// passed to STDIN, external symbolizer prints to STDOUT response:
|
||||
// <function_name>
|
||||
// <file_name>:<line_number>:<column_number>
|
||||
// <function_name>
|
||||
// <file_name>:<line_number>:<column_number>
|
||||
// ...
|
||||
// <empty line>
|
||||
// ExternalSymbolizer may not be used from two threads simultaneously.
|
||||
class ExternalSymbolizer {
|
||||
public:
|
||||
explicit ExternalSymbolizer(const char *path)
|
||||
: path_(path),
|
||||
input_fd_(kInvalidFd),
|
||||
output_fd_(kInvalidFd),
|
||||
times_restarted_(0),
|
||||
failed_to_start_(false) {
|
||||
CHECK(path_);
|
||||
CHECK_NE(path[0], '\0');
|
||||
}
|
||||
|
||||
char *SendCommand(bool is_data, const char *module_name, uptr module_offset) {
|
||||
for (; times_restarted_ < kMaxTimesRestarted; times_restarted_++) {
|
||||
// Start or restart symbolizer if we failed to send command to it.
|
||||
if (char *res = SendCommandImpl(is_data, module_name, module_offset))
|
||||
return res;
|
||||
Restart();
|
||||
}
|
||||
if (!failed_to_start_) {
|
||||
Report("WARNING: Failed to use and restart external symbolizer!\n");
|
||||
failed_to_start_ = true;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
private:
|
||||
bool Restart() {
|
||||
if (input_fd_ != kInvalidFd)
|
||||
internal_close(input_fd_);
|
||||
if (output_fd_ != kInvalidFd)
|
||||
internal_close(output_fd_);
|
||||
return StartSymbolizerSubprocess(path_, &input_fd_, &output_fd_);
|
||||
}
|
||||
|
||||
char *SendCommandImpl(bool is_data, const char *module_name,
|
||||
uptr module_offset) {
|
||||
if (input_fd_ == kInvalidFd || output_fd_ == kInvalidFd)
|
||||
return 0;
|
||||
CHECK(module_name);
|
||||
internal_snprintf(buffer_, kBufferSize, "%s\"%s\" 0x%zx\n",
|
||||
is_data ? "DATA " : "", module_name, module_offset);
|
||||
if (!writeToSymbolizer(buffer_, internal_strlen(buffer_)))
|
||||
return 0;
|
||||
if (!readFromSymbolizer(buffer_, kBufferSize))
|
||||
return 0;
|
||||
return buffer_;
|
||||
}
|
||||
|
||||
bool readFromSymbolizer(char *buffer, uptr max_length) {
|
||||
if (max_length == 0)
|
||||
return true;
|
||||
uptr read_len = 0;
|
||||
while (true) {
|
||||
uptr just_read = internal_read(input_fd_, buffer + read_len,
|
||||
max_length - read_len);
|
||||
// We can't read 0 bytes, as we don't expect external symbolizer to close
|
||||
// its stdout.
|
||||
if (just_read == 0 || just_read == (uptr)-1) {
|
||||
Report("WARNING: Can't read from symbolizer at fd %d\n", input_fd_);
|
||||
return false;
|
||||
}
|
||||
read_len += just_read;
|
||||
// Empty line marks the end of symbolizer output.
|
||||
if (read_len >= 2 && buffer[read_len - 1] == '\n' &&
|
||||
buffer[read_len - 2] == '\n') {
|
||||
break;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool writeToSymbolizer(const char *buffer, uptr length) {
|
||||
if (length == 0)
|
||||
return true;
|
||||
uptr write_len = internal_write(output_fd_, buffer, length);
|
||||
if (write_len == 0 || write_len == (uptr)-1) {
|
||||
Report("WARNING: Can't write to symbolizer at fd %d\n", output_fd_);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
virtual void ExecuteWithDefaultArgs(const char *path_to_binary) const {
|
||||
UNIMPLEMENTED();
|
||||
}
|
||||
|
||||
const char *path_;
|
||||
|
@ -298,8 +289,52 @@ class ExternalSymbolizer {
|
|||
char buffer_[kBufferSize];
|
||||
|
||||
static const uptr kMaxTimesRestarted = 5;
|
||||
static const int kSymbolizerStartupTimeMillis = 10;
|
||||
uptr times_restarted_;
|
||||
bool failed_to_start_;
|
||||
bool reported_invalid_path_;
|
||||
};
|
||||
|
||||
// For now we assume the following protocol:
|
||||
// For each request of the form
|
||||
// <module_name> <module_offset>
|
||||
// passed to STDIN, external symbolizer prints to STDOUT response:
|
||||
// <function_name>
|
||||
// <file_name>:<line_number>:<column_number>
|
||||
// <function_name>
|
||||
// <file_name>:<line_number>:<column_number>
|
||||
// ...
|
||||
// <empty line>
|
||||
class LLVMSymbolizerProcess : public SymbolizerProcess {
|
||||
public:
|
||||
explicit LLVMSymbolizerProcess(const char *path) : SymbolizerProcess(path) {}
|
||||
|
||||
private:
|
||||
bool RenderInputCommand(char *buffer, uptr max_length, bool is_data,
|
||||
const char *module_name, uptr module_offset) const {
|
||||
internal_snprintf(buffer, max_length, "%s\"%s\" 0x%zx\n",
|
||||
is_data ? "DATA " : "", module_name, module_offset);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ReachedEndOfOutput(const char *buffer, uptr length) const {
|
||||
// Empty line marks the end of llvm-symbolizer output.
|
||||
return length >= 2 && buffer[length - 1] == '\n' &&
|
||||
buffer[length - 2] == '\n';
|
||||
}
|
||||
|
||||
void ExecuteWithDefaultArgs(const char *path_to_binary) const {
|
||||
#if defined(__x86_64__)
|
||||
const char* const kSymbolizerArch = "--default-arch=x86_64";
|
||||
#elif defined(__i386__)
|
||||
const char* const kSymbolizerArch = "--default-arch=i386";
|
||||
#elif defined(__powerpc64__)
|
||||
const char* const kSymbolizerArch = "--default-arch=powerpc64";
|
||||
#else
|
||||
const char* const kSymbolizerArch = "--default-arch=unknown";
|
||||
#endif
|
||||
execl(path_to_binary, path_to_binary, kSymbolizerArch, (char *)0);
|
||||
}
|
||||
};
|
||||
|
||||
#if SANITIZER_SUPPORTS_WEAK_HOOKS
|
||||
|
@ -383,7 +418,7 @@ class InternalSymbolizer {
|
|||
|
||||
class POSIXSymbolizer : public Symbolizer {
|
||||
public:
|
||||
POSIXSymbolizer(ExternalSymbolizer *external_symbolizer,
|
||||
POSIXSymbolizer(SymbolizerProcess *external_symbolizer,
|
||||
InternalSymbolizer *internal_symbolizer,
|
||||
LibbacktraceSymbolizer *libbacktrace_symbolizer)
|
||||
: Symbolizer(),
|
||||
|
@ -595,7 +630,7 @@ class POSIXSymbolizer : public Symbolizer {
|
|||
bool modules_fresh_;
|
||||
BlockingMutex mu_;
|
||||
|
||||
ExternalSymbolizer *external_symbolizer_; // Leaked.
|
||||
SymbolizerProcess *external_symbolizer_; // Leaked.
|
||||
InternalSymbolizer *const internal_symbolizer_; // Leaked.
|
||||
LibbacktraceSymbolizer *libbacktrace_symbolizer_; // Leaked.
|
||||
};
|
||||
|
@ -603,7 +638,7 @@ class POSIXSymbolizer : public Symbolizer {
|
|||
Symbolizer *Symbolizer::PlatformInit(const char *path_to_external) {
|
||||
InternalSymbolizer* internal_symbolizer =
|
||||
InternalSymbolizer::get(&symbolizer_allocator_);
|
||||
ExternalSymbolizer *external_symbolizer = 0;
|
||||
SymbolizerProcess *external_symbolizer = 0;
|
||||
LibbacktraceSymbolizer *libbacktrace_symbolizer = 0;
|
||||
|
||||
if (!internal_symbolizer) {
|
||||
|
@ -615,7 +650,7 @@ Symbolizer *Symbolizer::PlatformInit(const char *path_to_external) {
|
|||
path_to_external = FindPathToBinary("llvm-symbolizer");
|
||||
if (path_to_external && path_to_external[0] != '\0')
|
||||
external_symbolizer = new(symbolizer_allocator_)
|
||||
ExternalSymbolizer(path_to_external);
|
||||
LLVMSymbolizerProcess(path_to_external);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue