summaryrefslogtreecommitdiffstats
path: root/compiler-rt/lib/tsan/rtl/tsan_printf.cc
blob: 2540d224c27d33f104d7a6c90f9bd4778d7d3d4c (plain)
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
//===-- tsan_printf.cc ----------------------------------------------------===//
//
//                     The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
//
// This file is a part of ThreadSanitizer (TSan), a race detector.
//
//===----------------------------------------------------------------------===//

#include "sanitizer_common/sanitizer_libc.h"
#include "tsan_defs.h"
#include "tsan_mman.h"
#include "tsan_platform.h"

#include <stdarg.h>  // va_list

typedef long long i64;  // NOLINT
typedef long iptr;  // NOLINT

namespace __tsan {

static int AppendChar(char **buff, const char *buff_end, char c) {
  if (*buff < buff_end) {
    **buff = c;
    (*buff)++;
  }
  return 1;
}

static int AppendUnsigned(char **buff, const char *buff_end, u64 num,
                          int base, uptr minimal_num_length) {
  uptr const kMaxLen = 30;
  uptr num_buffer[kMaxLen];
  uptr pos = 0;
  do {
    num_buffer[pos++] = num % base;
    num /= base;
  } while (num > 0);
  while (pos < minimal_num_length) num_buffer[pos++] = 0;
  int result = 0;
  while (pos-- > 0) {
    uptr digit = num_buffer[pos];
    result += AppendChar(buff, buff_end, (digit < 10) ? '0' + digit
                                                      : 'a' + digit - 10);
  }
  return result;
}

static int AppendSignedDecimal(char **buff, const char *buff_end, i64 num) {
  int result = 0;
  if (num < 0) {
    result += AppendChar(buff, buff_end, '-');
    num = -num;
  }
  result += AppendUnsigned(buff, buff_end, (u64)num, 10, 0);
  return result;
}

static int AppendString(char **buff, const char *buff_end, const char *s) {
  if (s == 0)
    s = "<null>";
  int result = 0;
  for (; *s; s++) {
    result += AppendChar(buff, buff_end, *s);
  }
  return result;
}

static int AppendPointer(char **buff, const char *buff_end, u64 ptr_value) {
  int result = 0;
  result += AppendString(buff, buff_end, "0x");
  result += AppendUnsigned(buff, buff_end, ptr_value, 16,
      (sizeof(void*) == 8) ? 12 : 8);  // NOLINT
  return result;
}

static uptr VSNPrintf(char *buff, int buff_length,
                     const char *format, va_list args) {
  const char *buff_end = &buff[buff_length - 1];
  const char *cur = format;
  int result = 0;
  for (; *cur; cur++) {
    if (*cur != '%') {
      result += AppendChar(&buff, buff_end, *cur);
      continue;
    }
    cur++;
    bool is_long = (*cur == 'l');
    cur += is_long;
    bool is_llong = (*cur == 'l');
    cur += is_llong;
    switch (*cur) {
      case 'd': {
        i64 v = is_llong ? va_arg(args, i64)
            : is_long ? va_arg(args, iptr)
            : va_arg(args, int);
        result += AppendSignedDecimal(&buff, buff_end, v);
        break;
      }
      case 'u':
      case 'x': {
        u64 v = is_llong ? va_arg(args, u64)
            : is_long ? va_arg(args, uptr)
            : va_arg(args, unsigned);
        result += AppendUnsigned(&buff, buff_end, v, *cur == 'u' ? 10: 16, 0);
        break;
      }
      case 'p': {
        result += AppendPointer(&buff, buff_end, va_arg(args, uptr));
        break;
      }
      case 's': {
        result += AppendString(&buff, buff_end, va_arg(args, char*));
        break;
      }
      default: {
        Die();
      }
    }
  }
  AppendChar(&buff, buff_end + 1, '\0');
  return result;
}

void Printf(const char *format, ...) {
  ScopedInRtl in_rtl;
  const uptr kMaxLen = 16 * 1024;
  InternalScopedBuf<char> buffer(kMaxLen);
  va_list args;
  va_start(args, format);
  uptr len = VSNPrintf(buffer, buffer.Size(), format, args);
  va_end(args);
  internal_write(CTX() ? flags()->log_fileno : 2,
      buffer, len < buffer.Size() ? len : buffer.Size() - 1);
}

uptr Snprintf(char *buffer, uptr length, const char *format, ...) {
  va_list args;
  va_start(args, format);
  uptr len = VSNPrintf(buffer, length, format, args);
  va_end(args);
  return len;
}
}
OpenPOWER on IntegriCloud