1353944Sdim//===-- sanitizer_symbolizer_libcdep.cpp ----------------------------------===//
2353944Sdim//
3353944Sdim// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4353944Sdim// See https://llvm.org/LICENSE.txt for license information.
5353944Sdim// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6353944Sdim//
7353944Sdim//===----------------------------------------------------------------------===//
8353944Sdim//
9353944Sdim// This file is shared between AddressSanitizer and ThreadSanitizer
10353944Sdim// run-time libraries.
11353944Sdim//===----------------------------------------------------------------------===//
12353944Sdim
13353944Sdim#include "sanitizer_allocator_internal.h"
14353944Sdim#include "sanitizer_internal_defs.h"
15353944Sdim#include "sanitizer_symbolizer_internal.h"
16353944Sdim
17353944Sdimnamespace __sanitizer {
18353944Sdim
19353944SdimSymbolizer *Symbolizer::GetOrInit() {
20353944Sdim  SpinMutexLock l(&init_mu_);
21353944Sdim  if (symbolizer_)
22353944Sdim    return symbolizer_;
23353944Sdim  symbolizer_ = PlatformInit();
24353944Sdim  CHECK(symbolizer_);
25353944Sdim  return symbolizer_;
26353944Sdim}
27353944Sdim
28353944Sdim// See sanitizer_symbolizer_markup.cpp.
29353944Sdim#if !SANITIZER_SYMBOLIZER_MARKUP
30353944Sdim
31353944Sdimconst char *ExtractToken(const char *str, const char *delims, char **result) {
32353944Sdim  uptr prefix_len = internal_strcspn(str, delims);
33353944Sdim  *result = (char*)InternalAlloc(prefix_len + 1);
34353944Sdim  internal_memcpy(*result, str, prefix_len);
35353944Sdim  (*result)[prefix_len] = '\0';
36353944Sdim  const char *prefix_end = str + prefix_len;
37353944Sdim  if (*prefix_end != '\0') prefix_end++;
38353944Sdim  return prefix_end;
39353944Sdim}
40353944Sdim
41353944Sdimconst char *ExtractInt(const char *str, const char *delims, int *result) {
42353944Sdim  char *buff;
43353944Sdim  const char *ret = ExtractToken(str, delims, &buff);
44353944Sdim  if (buff != 0) {
45353944Sdim    *result = (int)internal_atoll(buff);
46353944Sdim  }
47353944Sdim  InternalFree(buff);
48353944Sdim  return ret;
49353944Sdim}
50353944Sdim
51353944Sdimconst char *ExtractUptr(const char *str, const char *delims, uptr *result) {
52353944Sdim  char *buff;
53353944Sdim  const char *ret = ExtractToken(str, delims, &buff);
54353944Sdim  if (buff != 0) {
55353944Sdim    *result = (uptr)internal_atoll(buff);
56353944Sdim  }
57353944Sdim  InternalFree(buff);
58353944Sdim  return ret;
59353944Sdim}
60353944Sdim
61353944Sdimconst char *ExtractSptr(const char *str, const char *delims, sptr *result) {
62353944Sdim  char *buff;
63353944Sdim  const char *ret = ExtractToken(str, delims, &buff);
64353944Sdim  if (buff != 0) {
65353944Sdim    *result = (sptr)internal_atoll(buff);
66353944Sdim  }
67353944Sdim  InternalFree(buff);
68353944Sdim  return ret;
69353944Sdim}
70353944Sdim
71353944Sdimconst char *ExtractTokenUpToDelimiter(const char *str, const char *delimiter,
72353944Sdim                                      char **result) {
73353944Sdim  const char *found_delimiter = internal_strstr(str, delimiter);
74353944Sdim  uptr prefix_len =
75353944Sdim      found_delimiter ? found_delimiter - str : internal_strlen(str);
76353944Sdim  *result = (char *)InternalAlloc(prefix_len + 1);
77353944Sdim  internal_memcpy(*result, str, prefix_len);
78353944Sdim  (*result)[prefix_len] = '\0';
79353944Sdim  const char *prefix_end = str + prefix_len;
80353944Sdim  if (*prefix_end != '\0') prefix_end += internal_strlen(delimiter);
81353944Sdim  return prefix_end;
82353944Sdim}
83353944Sdim
84353944SdimSymbolizedStack *Symbolizer::SymbolizePC(uptr addr) {
85353944Sdim  BlockingMutexLock l(&mu_);
86353944Sdim  const char *module_name;
87353944Sdim  uptr module_offset;
88353944Sdim  ModuleArch arch;
89353944Sdim  SymbolizedStack *res = SymbolizedStack::New(addr);
90353944Sdim  if (!FindModuleNameAndOffsetForAddress(addr, &module_name, &module_offset,
91353944Sdim                                         &arch))
92353944Sdim    return res;
93353944Sdim  // Always fill data about module name and offset.
94353944Sdim  res->info.FillModuleInfo(module_name, module_offset, arch);
95353944Sdim  for (auto &tool : tools_) {
96353944Sdim    SymbolizerScope sym_scope(this);
97353944Sdim    if (tool.SymbolizePC(addr, res)) {
98353944Sdim      return res;
99353944Sdim    }
100353944Sdim  }
101353944Sdim  return res;
102353944Sdim}
103353944Sdim
104353944Sdimbool Symbolizer::SymbolizeData(uptr addr, DataInfo *info) {
105353944Sdim  BlockingMutexLock l(&mu_);
106353944Sdim  const char *module_name;
107353944Sdim  uptr module_offset;
108353944Sdim  ModuleArch arch;
109353944Sdim  if (!FindModuleNameAndOffsetForAddress(addr, &module_name, &module_offset,
110353944Sdim                                         &arch))
111353944Sdim    return false;
112353944Sdim  info->Clear();
113353944Sdim  info->module = internal_strdup(module_name);
114353944Sdim  info->module_offset = module_offset;
115353944Sdim  info->module_arch = arch;
116353944Sdim  for (auto &tool : tools_) {
117353944Sdim    SymbolizerScope sym_scope(this);
118353944Sdim    if (tool.SymbolizeData(addr, info)) {
119353944Sdim      return true;
120353944Sdim    }
121353944Sdim  }
122353944Sdim  return true;
123353944Sdim}
124353944Sdim
125353944Sdimbool Symbolizer::SymbolizeFrame(uptr addr, FrameInfo *info) {
126353944Sdim  BlockingMutexLock l(&mu_);
127353944Sdim  const char *module_name;
128353944Sdim  if (!FindModuleNameAndOffsetForAddress(
129353944Sdim          addr, &module_name, &info->module_offset, &info->module_arch))
130353944Sdim    return false;
131353944Sdim  info->module = internal_strdup(module_name);
132353944Sdim  for (auto &tool : tools_) {
133353944Sdim    SymbolizerScope sym_scope(this);
134353944Sdim    if (tool.SymbolizeFrame(addr, info)) {
135353944Sdim      return true;
136353944Sdim    }
137353944Sdim  }
138353944Sdim  return true;
139353944Sdim}
140353944Sdim
141353944Sdimbool Symbolizer::GetModuleNameAndOffsetForPC(uptr pc, const char **module_name,
142353944Sdim                                             uptr *module_address) {
143353944Sdim  BlockingMutexLock l(&mu_);
144353944Sdim  const char *internal_module_name = nullptr;
145353944Sdim  ModuleArch arch;
146353944Sdim  if (!FindModuleNameAndOffsetForAddress(pc, &internal_module_name,
147353944Sdim                                         module_address, &arch))
148353944Sdim    return false;
149353944Sdim
150353944Sdim  if (module_name)
151353944Sdim    *module_name = module_names_.GetOwnedCopy(internal_module_name);
152353944Sdim  return true;
153353944Sdim}
154353944Sdim
155353944Sdimvoid Symbolizer::Flush() {
156353944Sdim  BlockingMutexLock l(&mu_);
157353944Sdim  for (auto &tool : tools_) {
158353944Sdim    SymbolizerScope sym_scope(this);
159353944Sdim    tool.Flush();
160353944Sdim  }
161353944Sdim}
162353944Sdim
163353944Sdimconst char *Symbolizer::Demangle(const char *name) {
164353944Sdim  BlockingMutexLock l(&mu_);
165353944Sdim  for (auto &tool : tools_) {
166353944Sdim    SymbolizerScope sym_scope(this);
167353944Sdim    if (const char *demangled = tool.Demangle(name))
168353944Sdim      return demangled;
169353944Sdim  }
170353944Sdim  return PlatformDemangle(name);
171353944Sdim}
172353944Sdim
173353944Sdimbool Symbolizer::FindModuleNameAndOffsetForAddress(uptr address,
174353944Sdim                                                   const char **module_name,
175353944Sdim                                                   uptr *module_offset,
176353944Sdim                                                   ModuleArch *module_arch) {
177353944Sdim  const LoadedModule *module = FindModuleForAddress(address);
178353944Sdim  if (module == nullptr)
179353944Sdim    return false;
180353944Sdim  *module_name = module->full_name();
181353944Sdim  *module_offset = address - module->base_address();
182353944Sdim  *module_arch = module->arch();
183353944Sdim  return true;
184353944Sdim}
185353944Sdim
186353944Sdimvoid Symbolizer::RefreshModules() {
187353944Sdim  modules_.init();
188353944Sdim  fallback_modules_.fallbackInit();
189353944Sdim  RAW_CHECK(modules_.size() > 0);
190353944Sdim  modules_fresh_ = true;
191353944Sdim}
192353944Sdim
193353944Sdimstatic const LoadedModule *SearchForModule(const ListOfModules &modules,
194353944Sdim                                           uptr address) {
195353944Sdim  for (uptr i = 0; i < modules.size(); i++) {
196353944Sdim    if (modules[i].containsAddress(address)) {
197353944Sdim      return &modules[i];
198353944Sdim    }
199353944Sdim  }
200353944Sdim  return nullptr;
201353944Sdim}
202353944Sdim
203353944Sdimconst LoadedModule *Symbolizer::FindModuleForAddress(uptr address) {
204353944Sdim  bool modules_were_reloaded = false;
205353944Sdim  if (!modules_fresh_) {
206353944Sdim    RefreshModules();
207353944Sdim    modules_were_reloaded = true;
208353944Sdim  }
209353944Sdim  const LoadedModule *module = SearchForModule(modules_, address);
210353944Sdim  if (module) return module;
211353944Sdim
212353944Sdim  // dlopen/dlclose interceptors invalidate the module list, but when
213353944Sdim  // interception is disabled, we need to retry if the lookup fails in
214353944Sdim  // case the module list changed.
215353944Sdim#if !SANITIZER_INTERCEPT_DLOPEN_DLCLOSE
216353944Sdim  if (!modules_were_reloaded) {
217353944Sdim    RefreshModules();
218353944Sdim    module = SearchForModule(modules_, address);
219353944Sdim    if (module) return module;
220353944Sdim  }
221353944Sdim#endif
222353944Sdim
223353944Sdim  if (fallback_modules_.size()) {
224353944Sdim    module = SearchForModule(fallback_modules_, address);
225353944Sdim  }
226353944Sdim  return module;
227353944Sdim}
228353944Sdim
229353944Sdim// For now we assume the following protocol:
230353944Sdim// For each request of the form
231353944Sdim//   <module_name> <module_offset>
232353944Sdim// passed to STDIN, external symbolizer prints to STDOUT response:
233353944Sdim//   <function_name>
234353944Sdim//   <file_name>:<line_number>:<column_number>
235353944Sdim//   <function_name>
236353944Sdim//   <file_name>:<line_number>:<column_number>
237353944Sdim//   ...
238353944Sdim//   <empty line>
239353944Sdimclass LLVMSymbolizerProcess : public SymbolizerProcess {
240353944Sdim public:
241353944Sdim  explicit LLVMSymbolizerProcess(const char *path)
242353944Sdim      : SymbolizerProcess(path, /*use_posix_spawn=*/SANITIZER_MAC) {}
243353944Sdim
244353944Sdim private:
245353944Sdim  bool ReachedEndOfOutput(const char *buffer, uptr length) const override {
246353944Sdim    // Empty line marks the end of llvm-symbolizer output.
247353944Sdim    return length >= 2 && buffer[length - 1] == '\n' &&
248353944Sdim           buffer[length - 2] == '\n';
249353944Sdim  }
250353944Sdim
251353944Sdim  // When adding a new architecture, don't forget to also update
252353944Sdim  // script/asan_symbolize.py and sanitizer_common.h.
253353944Sdim  void GetArgV(const char *path_to_binary,
254353944Sdim               const char *(&argv)[kArgVMax]) const override {
255353944Sdim#if defined(__x86_64h__)
256353944Sdim    const char* const kSymbolizerArch = "--default-arch=x86_64h";
257353944Sdim#elif defined(__x86_64__)
258353944Sdim    const char* const kSymbolizerArch = "--default-arch=x86_64";
259353944Sdim#elif defined(__i386__)
260353944Sdim    const char* const kSymbolizerArch = "--default-arch=i386";
261353944Sdim#elif defined(__aarch64__)
262353944Sdim    const char* const kSymbolizerArch = "--default-arch=arm64";
263353944Sdim#elif defined(__arm__)
264353944Sdim    const char* const kSymbolizerArch = "--default-arch=arm";
265353944Sdim#elif defined(__powerpc64__) && __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__
266353944Sdim    const char* const kSymbolizerArch = "--default-arch=powerpc64";
267353944Sdim#elif defined(__powerpc64__) && __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
268353944Sdim    const char* const kSymbolizerArch = "--default-arch=powerpc64le";
269353944Sdim#elif defined(__s390x__)
270353944Sdim    const char* const kSymbolizerArch = "--default-arch=s390x";
271353944Sdim#elif defined(__s390__)
272353944Sdim    const char* const kSymbolizerArch = "--default-arch=s390";
273353944Sdim#else
274353944Sdim    const char* const kSymbolizerArch = "--default-arch=unknown";
275353944Sdim#endif
276353944Sdim
277353944Sdim    const char *const inline_flag = common_flags()->symbolize_inline_frames
278353944Sdim                                        ? "--inlining=true"
279353944Sdim                                        : "--inlining=false";
280353944Sdim    int i = 0;
281353944Sdim    argv[i++] = path_to_binary;
282353944Sdim    argv[i++] = inline_flag;
283353944Sdim    argv[i++] = kSymbolizerArch;
284353944Sdim    argv[i++] = nullptr;
285353944Sdim  }
286353944Sdim};
287353944Sdim
288353944SdimLLVMSymbolizer::LLVMSymbolizer(const char *path, LowLevelAllocator *allocator)
289353944Sdim    : symbolizer_process_(new(*allocator) LLVMSymbolizerProcess(path)) {}
290353944Sdim
291353944Sdim// Parse a <file>:<line>[:<column>] buffer. The file path may contain colons on
292353944Sdim// Windows, so extract tokens from the right hand side first. The column info is
293353944Sdim// also optional.
294353944Sdimstatic const char *ParseFileLineInfo(AddressInfo *info, const char *str) {
295353944Sdim  char *file_line_info = 0;
296353944Sdim  str = ExtractToken(str, "\n", &file_line_info);
297353944Sdim  CHECK(file_line_info);
298353944Sdim
299353944Sdim  if (uptr size = internal_strlen(file_line_info)) {
300353944Sdim    char *back = file_line_info + size - 1;
301353944Sdim    for (int i = 0; i < 2; ++i) {
302353944Sdim      while (back > file_line_info && IsDigit(*back)) --back;
303353944Sdim      if (*back != ':' || !IsDigit(back[1])) break;
304353944Sdim      info->column = info->line;
305353944Sdim      info->line = internal_atoll(back + 1);
306353944Sdim      // Truncate the string at the colon to keep only filename.
307353944Sdim      *back = '\0';
308353944Sdim      --back;
309353944Sdim    }
310353944Sdim    ExtractToken(file_line_info, "", &info->file);
311353944Sdim  }
312353944Sdim
313353944Sdim  InternalFree(file_line_info);
314353944Sdim  return str;
315353944Sdim}
316353944Sdim
317353944Sdim// Parses one or more two-line strings in the following format:
318353944Sdim//   <function_name>
319353944Sdim//   <file_name>:<line_number>[:<column_number>]
320353944Sdim// Used by LLVMSymbolizer, Addr2LinePool and InternalSymbolizer, since all of
321353944Sdim// them use the same output format.
322353944Sdimvoid ParseSymbolizePCOutput(const char *str, SymbolizedStack *res) {
323353944Sdim  bool top_frame = true;
324353944Sdim  SymbolizedStack *last = res;
325353944Sdim  while (true) {
326353944Sdim    char *function_name = 0;
327353944Sdim    str = ExtractToken(str, "\n", &function_name);
328353944Sdim    CHECK(function_name);
329353944Sdim    if (function_name[0] == '\0') {
330353944Sdim      // There are no more frames.
331353944Sdim      InternalFree(function_name);
332353944Sdim      break;
333353944Sdim    }
334353944Sdim    SymbolizedStack *cur;
335353944Sdim    if (top_frame) {
336353944Sdim      cur = res;
337353944Sdim      top_frame = false;
338353944Sdim    } else {
339353944Sdim      cur = SymbolizedStack::New(res->info.address);
340353944Sdim      cur->info.FillModuleInfo(res->info.module, res->info.module_offset,
341353944Sdim                               res->info.module_arch);
342353944Sdim      last->next = cur;
343353944Sdim      last = cur;
344353944Sdim    }
345353944Sdim
346353944Sdim    AddressInfo *info = &cur->info;
347353944Sdim    info->function = function_name;
348353944Sdim    str = ParseFileLineInfo(info, str);
349353944Sdim
350353944Sdim    // Functions and filenames can be "??", in which case we write 0
351353944Sdim    // to address info to mark that names are unknown.
352353944Sdim    if (0 == internal_strcmp(info->function, "??")) {
353353944Sdim      InternalFree(info->function);
354353944Sdim      info->function = 0;
355353944Sdim    }
356353944Sdim    if (0 == internal_strcmp(info->file, "??")) {
357353944Sdim      InternalFree(info->file);
358353944Sdim      info->file = 0;
359353944Sdim    }
360353944Sdim  }
361353944Sdim}
362353944Sdim
363353944Sdim// Parses a two-line string in the following format:
364353944Sdim//   <symbol_name>
365353944Sdim//   <start_address> <size>
366353944Sdim// Used by LLVMSymbolizer and InternalSymbolizer.
367353944Sdimvoid ParseSymbolizeDataOutput(const char *str, DataInfo *info) {
368353944Sdim  str = ExtractToken(str, "\n", &info->name);
369353944Sdim  str = ExtractUptr(str, " ", &info->start);
370353944Sdim  str = ExtractUptr(str, "\n", &info->size);
371353944Sdim}
372353944Sdim
373353944Sdimstatic void ParseSymbolizeFrameOutput(const char *str,
374353944Sdim                                      InternalMmapVector<LocalInfo> *locals) {
375353944Sdim  if (internal_strncmp(str, "??", 2) == 0)
376353944Sdim    return;
377353944Sdim
378353944Sdim  while (*str) {
379353944Sdim    LocalInfo local;
380353944Sdim    str = ExtractToken(str, "\n", &local.function_name);
381353944Sdim    str = ExtractToken(str, "\n", &local.name);
382353944Sdim
383353944Sdim    AddressInfo addr;
384353944Sdim    str = ParseFileLineInfo(&addr, str);
385353944Sdim    local.decl_file = addr.file;
386353944Sdim    local.decl_line = addr.line;
387353944Sdim
388353944Sdim    local.has_frame_offset = internal_strncmp(str, "??", 2) != 0;
389353944Sdim    str = ExtractSptr(str, " ", &local.frame_offset);
390353944Sdim
391353944Sdim    local.has_size = internal_strncmp(str, "??", 2) != 0;
392353944Sdim    str = ExtractUptr(str, " ", &local.size);
393353944Sdim
394353944Sdim    local.has_tag_offset = internal_strncmp(str, "??", 2) != 0;
395353944Sdim    str = ExtractUptr(str, "\n", &local.tag_offset);
396353944Sdim
397353944Sdim    locals->push_back(local);
398353944Sdim  }
399353944Sdim}
400353944Sdim
401353944Sdimbool LLVMSymbolizer::SymbolizePC(uptr addr, SymbolizedStack *stack) {
402353944Sdim  AddressInfo *info = &stack->info;
403353944Sdim  const char *buf = FormatAndSendCommand(
404353944Sdim      "CODE", info->module, info->module_offset, info->module_arch);
405353944Sdim  if (buf) {
406353944Sdim    ParseSymbolizePCOutput(buf, stack);
407353944Sdim    return true;
408353944Sdim  }
409353944Sdim  return false;
410353944Sdim}
411353944Sdim
412353944Sdimbool LLVMSymbolizer::SymbolizeData(uptr addr, DataInfo *info) {
413353944Sdim  const char *buf = FormatAndSendCommand(
414353944Sdim      "DATA", info->module, info->module_offset, info->module_arch);
415353944Sdim  if (buf) {
416353944Sdim    ParseSymbolizeDataOutput(buf, info);
417353944Sdim    info->start += (addr - info->module_offset); // Add the base address.
418353944Sdim    return true;
419353944Sdim  }
420353944Sdim  return false;
421353944Sdim}
422353944Sdim
423353944Sdimbool LLVMSymbolizer::SymbolizeFrame(uptr addr, FrameInfo *info) {
424353944Sdim  const char *buf = FormatAndSendCommand(
425353944Sdim      "FRAME", info->module, info->module_offset, info->module_arch);
426353944Sdim  if (buf) {
427353944Sdim    ParseSymbolizeFrameOutput(buf, &info->locals);
428353944Sdim    return true;
429353944Sdim  }
430353944Sdim  return false;
431353944Sdim}
432353944Sdim
433353944Sdimconst char *LLVMSymbolizer::FormatAndSendCommand(const char *command_prefix,
434353944Sdim                                                 const char *module_name,
435353944Sdim                                                 uptr module_offset,
436353944Sdim                                                 ModuleArch arch) {
437353944Sdim  CHECK(module_name);
438353944Sdim  if (arch == kModuleArchUnknown) {
439353944Sdim    if (internal_snprintf(buffer_, kBufferSize, "%s \"%s\" 0x%zx\n",
440353944Sdim                          command_prefix, module_name,
441353944Sdim                          module_offset) >= static_cast<int>(kBufferSize)) {
442353944Sdim      Report("WARNING: Command buffer too small");
443353944Sdim      return nullptr;
444353944Sdim    }
445353944Sdim  } else {
446353944Sdim    if (internal_snprintf(buffer_, kBufferSize, "%s \"%s:%s\" 0x%zx\n",
447353944Sdim                          command_prefix, module_name, ModuleArchToString(arch),
448353944Sdim                          module_offset) >= static_cast<int>(kBufferSize)) {
449353944Sdim      Report("WARNING: Command buffer too small");
450353944Sdim      return nullptr;
451353944Sdim    }
452353944Sdim  }
453353944Sdim  return symbolizer_process_->SendCommand(buffer_);
454353944Sdim}
455353944Sdim
456353944SdimSymbolizerProcess::SymbolizerProcess(const char *path, bool use_posix_spawn)
457353944Sdim    : path_(path),
458353944Sdim      input_fd_(kInvalidFd),
459353944Sdim      output_fd_(kInvalidFd),
460353944Sdim      times_restarted_(0),
461353944Sdim      failed_to_start_(false),
462353944Sdim      reported_invalid_path_(false),
463353944Sdim      use_posix_spawn_(use_posix_spawn) {
464353944Sdim  CHECK(path_);
465353944Sdim  CHECK_NE(path_[0], '\0');
466353944Sdim}
467353944Sdim
468353944Sdimstatic bool IsSameModule(const char* path) {
469353944Sdim  if (const char* ProcessName = GetProcessName()) {
470353944Sdim    if (const char* SymbolizerName = StripModuleName(path)) {
471353944Sdim      return !internal_strcmp(ProcessName, SymbolizerName);
472353944Sdim    }
473353944Sdim  }
474353944Sdim  return false;
475353944Sdim}
476353944Sdim
477353944Sdimconst char *SymbolizerProcess::SendCommand(const char *command) {
478353944Sdim  if (failed_to_start_)
479353944Sdim    return nullptr;
480353944Sdim  if (IsSameModule(path_)) {
481353944Sdim    Report("WARNING: Symbolizer was blocked from starting itself!\n");
482353944Sdim    failed_to_start_ = true;
483353944Sdim    return nullptr;
484353944Sdim  }
485353944Sdim  for (; times_restarted_ < kMaxTimesRestarted; times_restarted_++) {
486353944Sdim    // Start or restart symbolizer if we failed to send command to it.
487353944Sdim    if (const char *res = SendCommandImpl(command))
488353944Sdim      return res;
489353944Sdim    Restart();
490353944Sdim  }
491353944Sdim  if (!failed_to_start_) {
492353944Sdim    Report("WARNING: Failed to use and restart external symbolizer!\n");
493353944Sdim    failed_to_start_ = true;
494353944Sdim  }
495353944Sdim  return 0;
496353944Sdim}
497353944Sdim
498353944Sdimconst char *SymbolizerProcess::SendCommandImpl(const char *command) {
499353944Sdim  if (input_fd_ == kInvalidFd || output_fd_ == kInvalidFd)
500353944Sdim      return 0;
501353944Sdim  if (!WriteToSymbolizer(command, internal_strlen(command)))
502353944Sdim      return 0;
503353944Sdim  if (!ReadFromSymbolizer(buffer_, kBufferSize))
504353944Sdim      return 0;
505353944Sdim  return buffer_;
506353944Sdim}
507353944Sdim
508353944Sdimbool SymbolizerProcess::Restart() {
509353944Sdim  if (input_fd_ != kInvalidFd)
510353944Sdim    CloseFile(input_fd_);
511353944Sdim  if (output_fd_ != kInvalidFd)
512353944Sdim    CloseFile(output_fd_);
513353944Sdim  return StartSymbolizerSubprocess();
514353944Sdim}
515353944Sdim
516353944Sdimbool SymbolizerProcess::ReadFromSymbolizer(char *buffer, uptr max_length) {
517353944Sdim  if (max_length == 0)
518353944Sdim    return true;
519353944Sdim  uptr read_len = 0;
520353944Sdim  while (true) {
521353944Sdim    uptr just_read = 0;
522353944Sdim    bool success = ReadFromFile(input_fd_, buffer + read_len,
523353944Sdim                                max_length - read_len - 1, &just_read);
524353944Sdim    // We can't read 0 bytes, as we don't expect external symbolizer to close
525353944Sdim    // its stdout.
526353944Sdim    if (!success || just_read == 0) {
527353944Sdim      Report("WARNING: Can't read from symbolizer at fd %d\n", input_fd_);
528353944Sdim      return false;
529353944Sdim    }
530353944Sdim    read_len += just_read;
531353944Sdim    if (ReachedEndOfOutput(buffer, read_len))
532353944Sdim      break;
533353944Sdim    if (read_len + 1 == max_length) {
534353944Sdim      Report("WARNING: Symbolizer buffer too small\n");
535353944Sdim      read_len = 0;
536353944Sdim      break;
537353944Sdim    }
538353944Sdim  }
539353944Sdim  buffer[read_len] = '\0';
540353944Sdim  return true;
541353944Sdim}
542353944Sdim
543353944Sdimbool SymbolizerProcess::WriteToSymbolizer(const char *buffer, uptr length) {
544353944Sdim  if (length == 0)
545353944Sdim    return true;
546353944Sdim  uptr write_len = 0;
547353944Sdim  bool success = WriteToFile(output_fd_, buffer, length, &write_len);
548353944Sdim  if (!success || write_len != length) {
549353944Sdim    Report("WARNING: Can't write to symbolizer at fd %d\n", output_fd_);
550353944Sdim    return false;
551353944Sdim  }
552353944Sdim  return true;
553353944Sdim}
554353944Sdim
555353944Sdim#endif  // !SANITIZER_SYMBOLIZER_MARKUP
556353944Sdim
557353944Sdim}  // namespace __sanitizer
558