1*7c3d14c8STreehugger Robot //===-- sanitizer_printf.cc -----------------------------------------------===//
2*7c3d14c8STreehugger Robot //
3*7c3d14c8STreehugger Robot // The LLVM Compiler Infrastructure
4*7c3d14c8STreehugger Robot //
5*7c3d14c8STreehugger Robot // This file is distributed under the University of Illinois Open Source
6*7c3d14c8STreehugger Robot // License. See LICENSE.TXT for details.
7*7c3d14c8STreehugger Robot //
8*7c3d14c8STreehugger Robot //===----------------------------------------------------------------------===//
9*7c3d14c8STreehugger Robot //
10*7c3d14c8STreehugger Robot // This file is shared between AddressSanitizer and ThreadSanitizer.
11*7c3d14c8STreehugger Robot //
12*7c3d14c8STreehugger Robot // Internal printf function, used inside run-time libraries.
13*7c3d14c8STreehugger Robot // We can't use libc printf because we intercept some of the functions used
14*7c3d14c8STreehugger Robot // inside it.
15*7c3d14c8STreehugger Robot //===----------------------------------------------------------------------===//
16*7c3d14c8STreehugger Robot
17*7c3d14c8STreehugger Robot #include "sanitizer_common.h"
18*7c3d14c8STreehugger Robot #include "sanitizer_flags.h"
19*7c3d14c8STreehugger Robot #include "sanitizer_libc.h"
20*7c3d14c8STreehugger Robot
21*7c3d14c8STreehugger Robot #include <stdio.h>
22*7c3d14c8STreehugger Robot #include <stdarg.h>
23*7c3d14c8STreehugger Robot
24*7c3d14c8STreehugger Robot #if SANITIZER_WINDOWS && defined(_MSC_VER) && _MSC_VER < 1800 && \
25*7c3d14c8STreehugger Robot !defined(va_copy)
26*7c3d14c8STreehugger Robot # define va_copy(dst, src) ((dst) = (src))
27*7c3d14c8STreehugger Robot #endif
28*7c3d14c8STreehugger Robot
29*7c3d14c8STreehugger Robot namespace __sanitizer {
30*7c3d14c8STreehugger Robot
31*7c3d14c8STreehugger Robot StaticSpinMutex CommonSanitizerReportMutex;
32*7c3d14c8STreehugger Robot
AppendChar(char ** buff,const char * buff_end,char c)33*7c3d14c8STreehugger Robot static int AppendChar(char **buff, const char *buff_end, char c) {
34*7c3d14c8STreehugger Robot if (*buff < buff_end) {
35*7c3d14c8STreehugger Robot **buff = c;
36*7c3d14c8STreehugger Robot (*buff)++;
37*7c3d14c8STreehugger Robot }
38*7c3d14c8STreehugger Robot return 1;
39*7c3d14c8STreehugger Robot }
40*7c3d14c8STreehugger Robot
41*7c3d14c8STreehugger Robot // Appends number in a given base to buffer. If its length is less than
42*7c3d14c8STreehugger Robot // |minimal_num_length|, it is padded with leading zeroes or spaces, depending
43*7c3d14c8STreehugger Robot // on the value of |pad_with_zero|.
AppendNumber(char ** buff,const char * buff_end,u64 absolute_value,u8 base,u8 minimal_num_length,bool pad_with_zero,bool negative)44*7c3d14c8STreehugger Robot static int AppendNumber(char **buff, const char *buff_end, u64 absolute_value,
45*7c3d14c8STreehugger Robot u8 base, u8 minimal_num_length, bool pad_with_zero,
46*7c3d14c8STreehugger Robot bool negative) {
47*7c3d14c8STreehugger Robot uptr const kMaxLen = 30;
48*7c3d14c8STreehugger Robot RAW_CHECK(base == 10 || base == 16);
49*7c3d14c8STreehugger Robot RAW_CHECK(base == 10 || !negative);
50*7c3d14c8STreehugger Robot RAW_CHECK(absolute_value || !negative);
51*7c3d14c8STreehugger Robot RAW_CHECK(minimal_num_length < kMaxLen);
52*7c3d14c8STreehugger Robot int result = 0;
53*7c3d14c8STreehugger Robot if (negative && minimal_num_length)
54*7c3d14c8STreehugger Robot --minimal_num_length;
55*7c3d14c8STreehugger Robot if (negative && pad_with_zero)
56*7c3d14c8STreehugger Robot result += AppendChar(buff, buff_end, '-');
57*7c3d14c8STreehugger Robot uptr num_buffer[kMaxLen];
58*7c3d14c8STreehugger Robot int pos = 0;
59*7c3d14c8STreehugger Robot do {
60*7c3d14c8STreehugger Robot RAW_CHECK_MSG((uptr)pos < kMaxLen, "AppendNumber buffer overflow");
61*7c3d14c8STreehugger Robot num_buffer[pos++] = absolute_value % base;
62*7c3d14c8STreehugger Robot absolute_value /= base;
63*7c3d14c8STreehugger Robot } while (absolute_value > 0);
64*7c3d14c8STreehugger Robot if (pos < minimal_num_length) {
65*7c3d14c8STreehugger Robot // Make sure compiler doesn't insert call to memset here.
66*7c3d14c8STreehugger Robot internal_memset(&num_buffer[pos], 0,
67*7c3d14c8STreehugger Robot sizeof(num_buffer[0]) * (minimal_num_length - pos));
68*7c3d14c8STreehugger Robot pos = minimal_num_length;
69*7c3d14c8STreehugger Robot }
70*7c3d14c8STreehugger Robot RAW_CHECK(pos > 0);
71*7c3d14c8STreehugger Robot pos--;
72*7c3d14c8STreehugger Robot for (; pos >= 0 && num_buffer[pos] == 0; pos--) {
73*7c3d14c8STreehugger Robot char c = (pad_with_zero || pos == 0) ? '0' : ' ';
74*7c3d14c8STreehugger Robot result += AppendChar(buff, buff_end, c);
75*7c3d14c8STreehugger Robot }
76*7c3d14c8STreehugger Robot if (negative && !pad_with_zero) result += AppendChar(buff, buff_end, '-');
77*7c3d14c8STreehugger Robot for (; pos >= 0; pos--) {
78*7c3d14c8STreehugger Robot char digit = static_cast<char>(num_buffer[pos]);
79*7c3d14c8STreehugger Robot result += AppendChar(buff, buff_end, (digit < 10) ? '0' + digit
80*7c3d14c8STreehugger Robot : 'a' + digit - 10);
81*7c3d14c8STreehugger Robot }
82*7c3d14c8STreehugger Robot return result;
83*7c3d14c8STreehugger Robot }
84*7c3d14c8STreehugger Robot
AppendUnsigned(char ** buff,const char * buff_end,u64 num,u8 base,u8 minimal_num_length,bool pad_with_zero)85*7c3d14c8STreehugger Robot static int AppendUnsigned(char **buff, const char *buff_end, u64 num, u8 base,
86*7c3d14c8STreehugger Robot u8 minimal_num_length, bool pad_with_zero) {
87*7c3d14c8STreehugger Robot return AppendNumber(buff, buff_end, num, base, minimal_num_length,
88*7c3d14c8STreehugger Robot pad_with_zero, false /* negative */);
89*7c3d14c8STreehugger Robot }
90*7c3d14c8STreehugger Robot
AppendSignedDecimal(char ** buff,const char * buff_end,s64 num,u8 minimal_num_length,bool pad_with_zero)91*7c3d14c8STreehugger Robot static int AppendSignedDecimal(char **buff, const char *buff_end, s64 num,
92*7c3d14c8STreehugger Robot u8 minimal_num_length, bool pad_with_zero) {
93*7c3d14c8STreehugger Robot bool negative = (num < 0);
94*7c3d14c8STreehugger Robot return AppendNumber(buff, buff_end, (u64)(negative ? -num : num), 10,
95*7c3d14c8STreehugger Robot minimal_num_length, pad_with_zero, negative);
96*7c3d14c8STreehugger Robot }
97*7c3d14c8STreehugger Robot
AppendString(char ** buff,const char * buff_end,int precision,const char * s)98*7c3d14c8STreehugger Robot static int AppendString(char **buff, const char *buff_end, int precision,
99*7c3d14c8STreehugger Robot const char *s) {
100*7c3d14c8STreehugger Robot if (!s)
101*7c3d14c8STreehugger Robot s = "<null>";
102*7c3d14c8STreehugger Robot int result = 0;
103*7c3d14c8STreehugger Robot for (; *s; s++) {
104*7c3d14c8STreehugger Robot if (precision >= 0 && result >= precision)
105*7c3d14c8STreehugger Robot break;
106*7c3d14c8STreehugger Robot result += AppendChar(buff, buff_end, *s);
107*7c3d14c8STreehugger Robot }
108*7c3d14c8STreehugger Robot return result;
109*7c3d14c8STreehugger Robot }
110*7c3d14c8STreehugger Robot
AppendPointer(char ** buff,const char * buff_end,u64 ptr_value)111*7c3d14c8STreehugger Robot static int AppendPointer(char **buff, const char *buff_end, u64 ptr_value) {
112*7c3d14c8STreehugger Robot int result = 0;
113*7c3d14c8STreehugger Robot result += AppendString(buff, buff_end, -1, "0x");
114*7c3d14c8STreehugger Robot result += AppendUnsigned(buff, buff_end, ptr_value, 16,
115*7c3d14c8STreehugger Robot SANITIZER_POINTER_FORMAT_LENGTH, true);
116*7c3d14c8STreehugger Robot return result;
117*7c3d14c8STreehugger Robot }
118*7c3d14c8STreehugger Robot
VSNPrintf(char * buff,int buff_length,const char * format,va_list args)119*7c3d14c8STreehugger Robot int VSNPrintf(char *buff, int buff_length,
120*7c3d14c8STreehugger Robot const char *format, va_list args) {
121*7c3d14c8STreehugger Robot static const char *kPrintfFormatsHelp =
122*7c3d14c8STreehugger Robot "Supported Printf formats: %([0-9]*)?(z|ll)?{d,u,x}; %p; %(\\.\\*)?s; %c\n";
123*7c3d14c8STreehugger Robot RAW_CHECK(format);
124*7c3d14c8STreehugger Robot RAW_CHECK(buff_length > 0);
125*7c3d14c8STreehugger Robot const char *buff_end = &buff[buff_length - 1];
126*7c3d14c8STreehugger Robot const char *cur = format;
127*7c3d14c8STreehugger Robot int result = 0;
128*7c3d14c8STreehugger Robot for (; *cur; cur++) {
129*7c3d14c8STreehugger Robot if (*cur != '%') {
130*7c3d14c8STreehugger Robot result += AppendChar(&buff, buff_end, *cur);
131*7c3d14c8STreehugger Robot continue;
132*7c3d14c8STreehugger Robot }
133*7c3d14c8STreehugger Robot cur++;
134*7c3d14c8STreehugger Robot bool have_width = (*cur >= '0' && *cur <= '9');
135*7c3d14c8STreehugger Robot bool pad_with_zero = (*cur == '0');
136*7c3d14c8STreehugger Robot int width = 0;
137*7c3d14c8STreehugger Robot if (have_width) {
138*7c3d14c8STreehugger Robot while (*cur >= '0' && *cur <= '9') {
139*7c3d14c8STreehugger Robot width = width * 10 + *cur++ - '0';
140*7c3d14c8STreehugger Robot }
141*7c3d14c8STreehugger Robot }
142*7c3d14c8STreehugger Robot bool have_precision = (cur[0] == '.' && cur[1] == '*');
143*7c3d14c8STreehugger Robot int precision = -1;
144*7c3d14c8STreehugger Robot if (have_precision) {
145*7c3d14c8STreehugger Robot cur += 2;
146*7c3d14c8STreehugger Robot precision = va_arg(args, int);
147*7c3d14c8STreehugger Robot }
148*7c3d14c8STreehugger Robot bool have_z = (*cur == 'z');
149*7c3d14c8STreehugger Robot cur += have_z;
150*7c3d14c8STreehugger Robot bool have_ll = !have_z && (cur[0] == 'l' && cur[1] == 'l');
151*7c3d14c8STreehugger Robot cur += have_ll * 2;
152*7c3d14c8STreehugger Robot s64 dval;
153*7c3d14c8STreehugger Robot u64 uval;
154*7c3d14c8STreehugger Robot bool have_flags = have_width | have_z | have_ll;
155*7c3d14c8STreehugger Robot // Only %s supports precision for now
156*7c3d14c8STreehugger Robot CHECK(!(precision >= 0 && *cur != 's'));
157*7c3d14c8STreehugger Robot switch (*cur) {
158*7c3d14c8STreehugger Robot case 'd': {
159*7c3d14c8STreehugger Robot dval = have_ll ? va_arg(args, s64)
160*7c3d14c8STreehugger Robot : have_z ? va_arg(args, sptr)
161*7c3d14c8STreehugger Robot : va_arg(args, int);
162*7c3d14c8STreehugger Robot result += AppendSignedDecimal(&buff, buff_end, dval, width,
163*7c3d14c8STreehugger Robot pad_with_zero);
164*7c3d14c8STreehugger Robot break;
165*7c3d14c8STreehugger Robot }
166*7c3d14c8STreehugger Robot case 'u':
167*7c3d14c8STreehugger Robot case 'x': {
168*7c3d14c8STreehugger Robot uval = have_ll ? va_arg(args, u64)
169*7c3d14c8STreehugger Robot : have_z ? va_arg(args, uptr)
170*7c3d14c8STreehugger Robot : va_arg(args, unsigned);
171*7c3d14c8STreehugger Robot result += AppendUnsigned(&buff, buff_end, uval,
172*7c3d14c8STreehugger Robot (*cur == 'u') ? 10 : 16, width, pad_with_zero);
173*7c3d14c8STreehugger Robot break;
174*7c3d14c8STreehugger Robot }
175*7c3d14c8STreehugger Robot case 'p': {
176*7c3d14c8STreehugger Robot RAW_CHECK_MSG(!have_flags, kPrintfFormatsHelp);
177*7c3d14c8STreehugger Robot result += AppendPointer(&buff, buff_end, va_arg(args, uptr));
178*7c3d14c8STreehugger Robot break;
179*7c3d14c8STreehugger Robot }
180*7c3d14c8STreehugger Robot case 's': {
181*7c3d14c8STreehugger Robot RAW_CHECK_MSG(!have_flags, kPrintfFormatsHelp);
182*7c3d14c8STreehugger Robot result += AppendString(&buff, buff_end, precision, va_arg(args, char*));
183*7c3d14c8STreehugger Robot break;
184*7c3d14c8STreehugger Robot }
185*7c3d14c8STreehugger Robot case 'c': {
186*7c3d14c8STreehugger Robot RAW_CHECK_MSG(!have_flags, kPrintfFormatsHelp);
187*7c3d14c8STreehugger Robot result += AppendChar(&buff, buff_end, va_arg(args, int));
188*7c3d14c8STreehugger Robot break;
189*7c3d14c8STreehugger Robot }
190*7c3d14c8STreehugger Robot case '%' : {
191*7c3d14c8STreehugger Robot RAW_CHECK_MSG(!have_flags, kPrintfFormatsHelp);
192*7c3d14c8STreehugger Robot result += AppendChar(&buff, buff_end, '%');
193*7c3d14c8STreehugger Robot break;
194*7c3d14c8STreehugger Robot }
195*7c3d14c8STreehugger Robot default: {
196*7c3d14c8STreehugger Robot RAW_CHECK_MSG(false, kPrintfFormatsHelp);
197*7c3d14c8STreehugger Robot }
198*7c3d14c8STreehugger Robot }
199*7c3d14c8STreehugger Robot }
200*7c3d14c8STreehugger Robot RAW_CHECK(buff <= buff_end);
201*7c3d14c8STreehugger Robot AppendChar(&buff, buff_end + 1, '\0');
202*7c3d14c8STreehugger Robot return result;
203*7c3d14c8STreehugger Robot }
204*7c3d14c8STreehugger Robot
205*7c3d14c8STreehugger Robot static void (*PrintfAndReportCallback)(const char *);
SetPrintfAndReportCallback(void (* callback)(const char *))206*7c3d14c8STreehugger Robot void SetPrintfAndReportCallback(void (*callback)(const char *)) {
207*7c3d14c8STreehugger Robot PrintfAndReportCallback = callback;
208*7c3d14c8STreehugger Robot }
209*7c3d14c8STreehugger Robot
210*7c3d14c8STreehugger Robot // Can be overriden in frontend.
211*7c3d14c8STreehugger Robot #if SANITIZER_SUPPORTS_WEAK_HOOKS
212*7c3d14c8STreehugger Robot SANITIZER_INTERFACE_ATTRIBUTE SANITIZER_WEAK_ATTRIBUTE
OnPrint(const char * str)213*7c3d14c8STreehugger Robot void OnPrint(const char *str) {
214*7c3d14c8STreehugger Robot (void)str;
215*7c3d14c8STreehugger Robot }
216*7c3d14c8STreehugger Robot #elif defined(SANITIZER_GO) && defined(TSAN_EXTERNAL_HOOKS)
217*7c3d14c8STreehugger Robot void OnPrint(const char *str);
218*7c3d14c8STreehugger Robot #else
OnPrint(const char * str)219*7c3d14c8STreehugger Robot void OnPrint(const char *str) {
220*7c3d14c8STreehugger Robot (void)str;
221*7c3d14c8STreehugger Robot }
222*7c3d14c8STreehugger Robot #endif
223*7c3d14c8STreehugger Robot
CallPrintfAndReportCallback(const char * str)224*7c3d14c8STreehugger Robot static void CallPrintfAndReportCallback(const char *str) {
225*7c3d14c8STreehugger Robot OnPrint(str);
226*7c3d14c8STreehugger Robot if (PrintfAndReportCallback)
227*7c3d14c8STreehugger Robot PrintfAndReportCallback(str);
228*7c3d14c8STreehugger Robot }
229*7c3d14c8STreehugger Robot
SharedPrintfCode(bool append_pid,const char * format,va_list args)230*7c3d14c8STreehugger Robot static void SharedPrintfCode(bool append_pid, const char *format,
231*7c3d14c8STreehugger Robot va_list args) {
232*7c3d14c8STreehugger Robot va_list args2;
233*7c3d14c8STreehugger Robot va_copy(args2, args);
234*7c3d14c8STreehugger Robot const int kLen = 16 * 1024;
235*7c3d14c8STreehugger Robot // |local_buffer| is small enough not to overflow the stack and/or violate
236*7c3d14c8STreehugger Robot // the stack limit enforced by TSan (-Wframe-larger-than=512). On the other
237*7c3d14c8STreehugger Robot // hand, the bigger the buffer is, the more the chance the error report will
238*7c3d14c8STreehugger Robot // fit into it.
239*7c3d14c8STreehugger Robot char local_buffer[400];
240*7c3d14c8STreehugger Robot int needed_length;
241*7c3d14c8STreehugger Robot char *buffer = local_buffer;
242*7c3d14c8STreehugger Robot int buffer_size = ARRAY_SIZE(local_buffer);
243*7c3d14c8STreehugger Robot // First try to print a message using a local buffer, and then fall back to
244*7c3d14c8STreehugger Robot // mmaped buffer.
245*7c3d14c8STreehugger Robot for (int use_mmap = 0; use_mmap < 2; use_mmap++) {
246*7c3d14c8STreehugger Robot if (use_mmap) {
247*7c3d14c8STreehugger Robot va_end(args);
248*7c3d14c8STreehugger Robot va_copy(args, args2);
249*7c3d14c8STreehugger Robot buffer = (char*)MmapOrDie(kLen, "Report");
250*7c3d14c8STreehugger Robot buffer_size = kLen;
251*7c3d14c8STreehugger Robot }
252*7c3d14c8STreehugger Robot needed_length = 0;
253*7c3d14c8STreehugger Robot // Check that data fits into the current buffer.
254*7c3d14c8STreehugger Robot # define CHECK_NEEDED_LENGTH \
255*7c3d14c8STreehugger Robot if (needed_length >= buffer_size) { \
256*7c3d14c8STreehugger Robot if (!use_mmap) continue; \
257*7c3d14c8STreehugger Robot RAW_CHECK_MSG(needed_length < kLen, \
258*7c3d14c8STreehugger Robot "Buffer in Report is too short!\n"); \
259*7c3d14c8STreehugger Robot }
260*7c3d14c8STreehugger Robot if (append_pid) {
261*7c3d14c8STreehugger Robot int pid = internal_getpid();
262*7c3d14c8STreehugger Robot const char *exe_name = GetProcessName();
263*7c3d14c8STreehugger Robot if (common_flags()->log_exe_name && exe_name) {
264*7c3d14c8STreehugger Robot needed_length += internal_snprintf(buffer, buffer_size,
265*7c3d14c8STreehugger Robot "==%s", exe_name);
266*7c3d14c8STreehugger Robot CHECK_NEEDED_LENGTH
267*7c3d14c8STreehugger Robot }
268*7c3d14c8STreehugger Robot needed_length += internal_snprintf(buffer + needed_length,
269*7c3d14c8STreehugger Robot buffer_size - needed_length,
270*7c3d14c8STreehugger Robot "==%d==", pid);
271*7c3d14c8STreehugger Robot CHECK_NEEDED_LENGTH
272*7c3d14c8STreehugger Robot }
273*7c3d14c8STreehugger Robot needed_length += VSNPrintf(buffer + needed_length,
274*7c3d14c8STreehugger Robot buffer_size - needed_length, format, args);
275*7c3d14c8STreehugger Robot CHECK_NEEDED_LENGTH
276*7c3d14c8STreehugger Robot // If the message fit into the buffer, print it and exit.
277*7c3d14c8STreehugger Robot break;
278*7c3d14c8STreehugger Robot # undef CHECK_NEEDED_LENGTH
279*7c3d14c8STreehugger Robot }
280*7c3d14c8STreehugger Robot RawWrite(buffer);
281*7c3d14c8STreehugger Robot
282*7c3d14c8STreehugger Robot // Remove color sequences from the message.
283*7c3d14c8STreehugger Robot RemoveANSIEscapeSequencesFromString(buffer);
284*7c3d14c8STreehugger Robot CallPrintfAndReportCallback(buffer);
285*7c3d14c8STreehugger Robot LogMessageOnPrintf(buffer);
286*7c3d14c8STreehugger Robot
287*7c3d14c8STreehugger Robot // If we had mapped any memory, clean up.
288*7c3d14c8STreehugger Robot if (buffer != local_buffer)
289*7c3d14c8STreehugger Robot UnmapOrDie((void *)buffer, buffer_size);
290*7c3d14c8STreehugger Robot va_end(args2);
291*7c3d14c8STreehugger Robot }
292*7c3d14c8STreehugger Robot
293*7c3d14c8STreehugger Robot FORMAT(1, 2)
Printf(const char * format,...)294*7c3d14c8STreehugger Robot void Printf(const char *format, ...) {
295*7c3d14c8STreehugger Robot va_list args;
296*7c3d14c8STreehugger Robot va_start(args, format);
297*7c3d14c8STreehugger Robot SharedPrintfCode(false, format, args);
298*7c3d14c8STreehugger Robot va_end(args);
299*7c3d14c8STreehugger Robot }
300*7c3d14c8STreehugger Robot
301*7c3d14c8STreehugger Robot // Like Printf, but prints the current PID before the output string.
302*7c3d14c8STreehugger Robot FORMAT(1, 2)
Report(const char * format,...)303*7c3d14c8STreehugger Robot void Report(const char *format, ...) {
304*7c3d14c8STreehugger Robot va_list args;
305*7c3d14c8STreehugger Robot va_start(args, format);
306*7c3d14c8STreehugger Robot SharedPrintfCode(true, format, args);
307*7c3d14c8STreehugger Robot va_end(args);
308*7c3d14c8STreehugger Robot }
309*7c3d14c8STreehugger Robot
310*7c3d14c8STreehugger Robot // Writes at most "length" symbols to "buffer" (including trailing '\0').
311*7c3d14c8STreehugger Robot // Returns the number of symbols that should have been written to buffer
312*7c3d14c8STreehugger Robot // (not including trailing '\0'). Thus, the string is truncated
313*7c3d14c8STreehugger Robot // iff return value is not less than "length".
314*7c3d14c8STreehugger Robot FORMAT(3, 4)
internal_snprintf(char * buffer,uptr length,const char * format,...)315*7c3d14c8STreehugger Robot int internal_snprintf(char *buffer, uptr length, const char *format, ...) {
316*7c3d14c8STreehugger Robot va_list args;
317*7c3d14c8STreehugger Robot va_start(args, format);
318*7c3d14c8STreehugger Robot int needed_length = VSNPrintf(buffer, length, format, args);
319*7c3d14c8STreehugger Robot va_end(args);
320*7c3d14c8STreehugger Robot return needed_length;
321*7c3d14c8STreehugger Robot }
322*7c3d14c8STreehugger Robot
323*7c3d14c8STreehugger Robot FORMAT(2, 3)
append(const char * format,...)324*7c3d14c8STreehugger Robot void InternalScopedString::append(const char *format, ...) {
325*7c3d14c8STreehugger Robot CHECK_LT(length_, size());
326*7c3d14c8STreehugger Robot va_list args;
327*7c3d14c8STreehugger Robot va_start(args, format);
328*7c3d14c8STreehugger Robot VSNPrintf(data() + length_, size() - length_, format, args);
329*7c3d14c8STreehugger Robot va_end(args);
330*7c3d14c8STreehugger Robot length_ += internal_strlen(data() + length_);
331*7c3d14c8STreehugger Robot CHECK_LT(length_, size());
332*7c3d14c8STreehugger Robot }
333*7c3d14c8STreehugger Robot
334*7c3d14c8STreehugger Robot } // namespace __sanitizer
335