reference, declarationdefinition
definition → references, declarations, derived classes, virtual overrides
reference to multiple definitions → definitions
unreferenced
    1
    2
    3
    4
    5
    6
    7
    8
    9
   10
   11
   12
   13
   14
   15
   16
   17
   18
   19
   20
   21
   22
   23
   24
   25
   26
   27
   28
   29
   30
   31
   32
   33
   34
   35
   36
   37
   38
   39
   40
   41
   42
   43
   44
   45
   46
   47
   48
   49
   50
   51
   52
   53
   54
   55
   56
   57
   58
   59
   60
   61
   62
   63
   64
   65
   66
   67
   68
   69
   70
   71
   72
   73
   74
   75
   76
   77
   78
   79
   80
   81
   82
   83
   84
   85
   86
   87
   88
   89
   90
   91
   92
   93
   94
   95
   96
   97
   98
   99
  100
  101
  102
  103
  104
  105
  106
  107
  108
  109
  110
  111
  112
  113
  114
  115
  116
  117
  118
  119
  120
  121
  122
  123
  124
  125
  126
  127
  128
  129
  130
  131
  132
  133
  134
  135
  136
  137
  138
  139
  140
  141
  142
  143
  144
  145
  146
  147
  148
  149
  150
  151
  152
  153
  154
  155
  156
  157
  158
  159
  160
  161
  162
  163
  164
  165
  166
  167
  168
  169
  170
  171
  172
  173
  174
  175
  176
  177
  178
  179
  180
  181
  182
  183
  184
  185
  186
  187
  188
  189
  190
  191
  192
  193
  194
  195
/*===- InstrProfilingPlatformFuchsia.c - Profile data Fuchsia platform ----===*\
|*
|* Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|* See https://llvm.org/LICENSE.txt for license information.
|* SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|*
\*===----------------------------------------------------------------------===*/
/*
 * This file implements the profiling runtime for Fuchsia and defines the
 * shared profile runtime interface. Each module (executable or DSO) statically
 * links in the whole profile runtime to satisfy the calls from its
 * instrumented code. Several modules in the same program might be separately
 * compiled and even use different versions of the instrumentation ABI and data
 * format. All they share in common is the VMO and the offset, which live in
 * exported globals so that exactly one definition will be shared across all
 * modules. Each module has its own independent runtime that registers its own
 * atexit hook to append its own data into the shared VMO which is published
 * via the data sink hook provided by Fuchsia's dynamic linker.
 */

#if defined(__Fuchsia__)

#include <inttypes.h>
#include <stdarg.h>
#include <stdbool.h>
#include <stdlib.h>

#include <zircon/process.h>
#include <zircon/sanitizer.h>
#include <zircon/status.h>
#include <zircon/syscalls.h>

#include "InstrProfiling.h"
#include "InstrProfilingInternal.h"
#include "InstrProfilingUtil.h"

/* VMO that contains the coverage data shared across all modules. This symbol
 * has default visibility and is exported in each module (executable or DSO)
 * that statically links in the profiling runtime.
 */
zx_handle_t __llvm_profile_vmo;
/* Current offset within the VMO where data should be written next. This symbol
 * has default visibility and is exported in each module (executable or DSO)
 * that statically links in the profiling runtime.
 */
uint64_t __llvm_profile_offset;

static const char ProfileSinkName[] = "llvm-profile";

static inline void lprofWrite(const char *fmt, ...) {
  char s[256];

  va_list ap;
  va_start(ap, fmt);
  int ret = vsnprintf(s, sizeof(s), fmt, ap);
  va_end(ap);

  __sanitizer_log_write(s, ret + 1);
}

static void createVMO() {
  /* Don't create VMO if it has been alread created. */
  if (__llvm_profile_vmo != ZX_HANDLE_INVALID)
    return;

  /* Get information about the current process. */
  zx_info_handle_basic_t Info;
  zx_status_t Status =
      _zx_object_get_info(_zx_process_self(), ZX_INFO_HANDLE_BASIC, &Info,
                          sizeof(Info), NULL, NULL);
  if (Status != ZX_OK) {
    lprofWrite("LLVM Profile: cannot get info about current process: %s\n",
               _zx_status_get_string(Status));
    return;
  }

  /* Create VMO to hold the profile data. */
  Status = _zx_vmo_create(0, ZX_VMO_RESIZABLE, &__llvm_profile_vmo);
  if (Status != ZX_OK) {
    lprofWrite("LLVM Profile: cannot create VMO: %s\n",
               _zx_status_get_string(Status));
    return;
  }

  /* Give the VMO a name including our process KOID so it's easy to spot. */
  char VmoName[ZX_MAX_NAME_LEN];
  snprintf(VmoName, sizeof(VmoName), "%s.%" PRIu64, ProfileSinkName, Info.koid);
  _zx_object_set_property(__llvm_profile_vmo, ZX_PROP_NAME, VmoName,
                          strlen(VmoName));

  /* Duplicate the handle since __sanitizer_publish_data consumes it. */
  zx_handle_t Handle;
  Status =
      _zx_handle_duplicate(__llvm_profile_vmo, ZX_RIGHT_SAME_RIGHTS, &Handle);
  if (Status != ZX_OK) {
    lprofWrite("LLVM Profile: cannot duplicate VMO handle: %s\n",
               _zx_status_get_string(Status));
    _zx_handle_close(__llvm_profile_vmo);
    __llvm_profile_vmo = ZX_HANDLE_INVALID;
    return;
  }

  /* Publish the VMO which contains profile data to the system. */
  __sanitizer_publish_data(ProfileSinkName, Handle);

  /* Use the dumpfile symbolizer markup element to write the name of VMO. */
  lprofWrite("LLVM Profile: {{{dumpfile:%s:%s}}}\n", ProfileSinkName, VmoName);
}

static uint32_t lprofVMOWriter(ProfDataWriter *This, ProfDataIOVec *IOVecs,
                               uint32_t NumIOVecs) {
  /* Compute the total length of data to be written. */
  size_t Length = 0;
  for (uint32_t I = 0; I < NumIOVecs; I++)
    Length += IOVecs[I].ElmSize * IOVecs[I].NumElm;

  /* Resize the VMO to ensure there's sufficient space for the data. */
  zx_status_t Status =
      _zx_vmo_set_size(__llvm_profile_vmo, __llvm_profile_offset + Length);
  if (Status != ZX_OK)
    return -1;

  /* Copy the data into VMO. */
  for (uint32_t I = 0; I < NumIOVecs; I++) {
    size_t Length = IOVecs[I].ElmSize * IOVecs[I].NumElm;
    if (IOVecs[I].Data) {
      Status = _zx_vmo_write(__llvm_profile_vmo, IOVecs[I].Data,
                             __llvm_profile_offset, Length);
      if (Status != ZX_OK)
        return -1;
    }
    __llvm_profile_offset += Length;
  }

  return 0;
}

static void initVMOWriter(ProfDataWriter *This) {
  This->Write = lprofVMOWriter;
  This->WriterCtx = NULL;
}

static int dump(void) {
  if (lprofProfileDumped()) {
    lprofWrite("LLVM Profile: data not published: already written.\n");
    return 0;
  }

  /* Check if there is llvm/runtime version mismatch. */
  if (GET_VERSION(__llvm_profile_get_version()) != INSTR_PROF_RAW_VERSION) {
    lprofWrite("LLVM Profile: runtime and instrumentation version mismatch: "
               "expected %d, but got %d\n",
               INSTR_PROF_RAW_VERSION,
               (int)GET_VERSION(__llvm_profile_get_version()));
    return -1;
  }

  /* Write the profile data into the mapped region. */
  ProfDataWriter VMOWriter;
  initVMOWriter(&VMOWriter);
  if (lprofWriteData(&VMOWriter, lprofGetVPDataReader(), 0) != 0)
    return -1;

  return 0;
}

COMPILER_RT_VISIBILITY
int __llvm_profile_dump(void) {
  int rc = dump();
  lprofSetProfileDumped();
  return rc;
}

static void dumpWithoutReturn(void) { dump(); }

/* This method is invoked by the runtime initialization hook
 * InstrProfilingRuntime.o if it is linked in.
 */
COMPILER_RT_VISIBILITY
void __llvm_profile_initialize_file(void) { createVMO(); }

COMPILER_RT_VISIBILITY
int __llvm_profile_register_write_file_atexit(void) {
  static bool HasBeenRegistered = false;

  if (HasBeenRegistered)
    return 0;

  lprofSetupValueProfiler();

  HasBeenRegistered = true;
  return atexit(dumpWithoutReturn);
}

#endif