[UBSan] Respect runtime flag for colorizing reports
[blocksruntime.git] / lib / ubsan / ubsan_diag.cc
blobba31f4caaae7376e1d70d9f044b06504181e48a2
1 //===-- ubsan_diag.cc -----------------------------------------------------===//
2 //
3 // The LLVM Compiler Infrastructure
4 //
5 // This file is distributed under the University of Illinois Open Source
6 // License. See LICENSE.TXT for details.
7 //
8 //===----------------------------------------------------------------------===//
9 //
10 // Diagnostic reporting for the UBSan runtime.
12 //===----------------------------------------------------------------------===//
14 #include "ubsan_diag.h"
15 #include "sanitizer_common/sanitizer_common.h"
16 #include "sanitizer_common/sanitizer_flags.h"
17 #include "sanitizer_common/sanitizer_libc.h"
18 #include "sanitizer_common/sanitizer_report_decorator.h"
19 #include "sanitizer_common/sanitizer_stacktrace.h"
20 #include "sanitizer_common/sanitizer_symbolizer.h"
21 #include <stdio.h>
23 using namespace __ubsan;
25 static void InitializeSanitizerCommon() {
26 static StaticSpinMutex init_mu;
27 SpinMutexLock l(&init_mu);
28 static bool initialized;
29 if (initialized)
30 return;
31 if (0 == internal_strcmp(SanitizerToolName, "SanitizerTool")) {
32 // UBSan is run in a standalone mode. Initialize it now.
33 SanitizerToolName = "UndefinedBehaviorSanitizer";
34 CommonFlags *cf = common_flags();
35 SetCommonFlagsDefaults(cf);
36 cf->print_summary = false;
38 initialized = true;
41 Location __ubsan::getCallerLocation(uptr CallerLoc) {
42 if (!CallerLoc)
43 return Location();
45 uptr Loc = StackTrace::GetPreviousInstructionPc(CallerLoc);
46 return getFunctionLocation(Loc, 0);
49 Location __ubsan::getFunctionLocation(uptr Loc, const char **FName) {
50 if (!Loc)
51 return Location();
52 InitializeSanitizerCommon();
54 AddressInfo Info;
55 if (!Symbolizer::GetOrInit()->SymbolizePC(Loc, &Info, 1) ||
56 !Info.module || !*Info.module)
57 return Location(Loc);
59 if (FName && Info.function)
60 *FName = Info.function;
62 if (!Info.file)
63 return ModuleLocation(Info.module, Info.module_offset);
65 return SourceLocation(Info.file, Info.line, Info.column);
68 Diag &Diag::operator<<(const TypeDescriptor &V) {
69 return AddArg(V.getTypeName());
72 Diag &Diag::operator<<(const Value &V) {
73 if (V.getType().isSignedIntegerTy())
74 AddArg(V.getSIntValue());
75 else if (V.getType().isUnsignedIntegerTy())
76 AddArg(V.getUIntValue());
77 else if (V.getType().isFloatTy())
78 AddArg(V.getFloatValue());
79 else
80 AddArg("<unknown>");
81 return *this;
84 /// Hexadecimal printing for numbers too large for Printf to handle directly.
85 static void PrintHex(UIntMax Val) {
86 #if HAVE_INT128_T
87 Printf("0x%08x%08x%08x%08x",
88 (unsigned int)(Val >> 96),
89 (unsigned int)(Val >> 64),
90 (unsigned int)(Val >> 32),
91 (unsigned int)(Val));
92 #else
93 UNREACHABLE("long long smaller than 64 bits?");
94 #endif
97 static void renderLocation(Location Loc) {
98 InternalScopedString LocBuffer(1024);
99 switch (Loc.getKind()) {
100 case Location::LK_Source: {
101 SourceLocation SLoc = Loc.getSourceLocation();
102 if (SLoc.isInvalid())
103 LocBuffer.append("<unknown>");
104 else
105 PrintSourceLocation(&LocBuffer, SLoc.getFilename(), SLoc.getLine(),
106 SLoc.getColumn());
107 break;
109 case Location::LK_Module:
110 PrintModuleAndOffset(&LocBuffer, Loc.getModuleLocation().getModuleName(),
111 Loc.getModuleLocation().getOffset());
112 break;
113 case Location::LK_Memory:
114 LocBuffer.append("%p", Loc.getMemoryLocation());
115 break;
116 case Location::LK_Null:
117 LocBuffer.append("<unknown>");
118 break;
120 Printf("%s:", LocBuffer.data());
123 static void renderText(const char *Message, const Diag::Arg *Args) {
124 for (const char *Msg = Message; *Msg; ++Msg) {
125 if (*Msg != '%') {
126 char Buffer[64];
127 unsigned I;
128 for (I = 0; Msg[I] && Msg[I] != '%' && I != 63; ++I)
129 Buffer[I] = Msg[I];
130 Buffer[I] = '\0';
131 Printf(Buffer);
132 Msg += I - 1;
133 } else {
134 const Diag::Arg &A = Args[*++Msg - '0'];
135 switch (A.Kind) {
136 case Diag::AK_String:
137 Printf("%s", A.String);
138 break;
139 case Diag::AK_Mangled: {
140 Printf("'%s'", Symbolizer::GetOrInit()->Demangle(A.String));
141 break;
143 case Diag::AK_SInt:
144 // 'long long' is guaranteed to be at least 64 bits wide.
145 if (A.SInt >= INT64_MIN && A.SInt <= INT64_MAX)
146 Printf("%lld", (long long)A.SInt);
147 else
148 PrintHex(A.SInt);
149 break;
150 case Diag::AK_UInt:
151 if (A.UInt <= UINT64_MAX)
152 Printf("%llu", (unsigned long long)A.UInt);
153 else
154 PrintHex(A.UInt);
155 break;
156 case Diag::AK_Float: {
157 // FIXME: Support floating-point formatting in sanitizer_common's
158 // printf, and stop using snprintf here.
159 char Buffer[32];
160 snprintf(Buffer, sizeof(Buffer), "%Lg", (long double)A.Float);
161 Printf("%s", Buffer);
162 break;
164 case Diag::AK_Pointer:
165 Printf("%p", A.Pointer);
166 break;
172 /// Find the earliest-starting range in Ranges which ends after Loc.
173 static Range *upperBound(MemoryLocation Loc, Range *Ranges,
174 unsigned NumRanges) {
175 Range *Best = 0;
176 for (unsigned I = 0; I != NumRanges; ++I)
177 if (Ranges[I].getEnd().getMemoryLocation() > Loc &&
178 (!Best ||
179 Best->getStart().getMemoryLocation() >
180 Ranges[I].getStart().getMemoryLocation()))
181 Best = &Ranges[I];
182 return Best;
185 /// Render a snippet of the address space near a location.
186 static void renderMemorySnippet(const __sanitizer::AnsiColorDecorator &Decor,
187 MemoryLocation Loc,
188 Range *Ranges, unsigned NumRanges,
189 const Diag::Arg *Args) {
190 const unsigned BytesToShow = 32;
191 const unsigned MinBytesNearLoc = 4;
193 // Show at least the 8 bytes surrounding Loc.
194 MemoryLocation Min = Loc - MinBytesNearLoc, Max = Loc + MinBytesNearLoc;
195 for (unsigned I = 0; I < NumRanges; ++I) {
196 Min = __sanitizer::Min(Ranges[I].getStart().getMemoryLocation(), Min);
197 Max = __sanitizer::Max(Ranges[I].getEnd().getMemoryLocation(), Max);
200 // If we have too many interesting bytes, prefer to show bytes after Loc.
201 if (Max - Min > BytesToShow)
202 Min = __sanitizer::Min(Max - BytesToShow, Loc - MinBytesNearLoc);
203 Max = Min + BytesToShow;
205 // Emit data.
206 for (uptr P = Min; P != Max; ++P) {
207 // FIXME: Check that the address is readable before printing it.
208 unsigned char C = *reinterpret_cast<const unsigned char*>(P);
209 Printf("%s%02x", (P % 8 == 0) ? " " : " ", C);
211 Printf("\n");
213 // Emit highlights.
214 Printf(Decor.Green());
215 Range *InRange = upperBound(Min, Ranges, NumRanges);
216 for (uptr P = Min; P != Max; ++P) {
217 char Pad = ' ', Byte = ' ';
218 if (InRange && InRange->getEnd().getMemoryLocation() == P)
219 InRange = upperBound(P, Ranges, NumRanges);
220 if (!InRange && P > Loc)
221 break;
222 if (InRange && InRange->getStart().getMemoryLocation() < P)
223 Pad = '~';
224 if (InRange && InRange->getStart().getMemoryLocation() <= P)
225 Byte = '~';
226 char Buffer[] = { Pad, Pad, P == Loc ? '^' : Byte, Byte, 0 };
227 Printf((P % 8 == 0) ? Buffer : &Buffer[1]);
229 Printf("%s\n", Decor.Default());
231 // Go over the line again, and print names for the ranges.
232 InRange = 0;
233 unsigned Spaces = 0;
234 for (uptr P = Min; P != Max; ++P) {
235 if (!InRange || InRange->getEnd().getMemoryLocation() == P)
236 InRange = upperBound(P, Ranges, NumRanges);
237 if (!InRange)
238 break;
240 Spaces += (P % 8) == 0 ? 2 : 1;
242 if (InRange && InRange->getStart().getMemoryLocation() == P) {
243 while (Spaces--)
244 Printf(" ");
245 renderText(InRange->getText(), Args);
246 Printf("\n");
247 // FIXME: We only support naming one range for now!
248 break;
251 Spaces += 2;
254 // FIXME: Print names for anything we can identify within the line:
256 // * If we can identify the memory itself as belonging to a particular
257 // global, stack variable, or dynamic allocation, then do so.
259 // * If we have a pointer-size, pointer-aligned range highlighted,
260 // determine whether the value of that range is a pointer to an
261 // entity which we can name, and if so, print that name.
263 // This needs an external symbolizer, or (preferably) ASan instrumentation.
266 Diag::~Diag() {
267 InitializeSanitizerCommon();
268 __sanitizer::AnsiColorDecorator Decor(ColorizeReports());
269 SpinMutexLock l(&CommonSanitizerReportMutex);
270 Printf(Decor.Bold());
272 renderLocation(Loc);
274 switch (Level) {
275 case DL_Error:
276 Printf("%s runtime error: %s%s",
277 Decor.Red(), Decor.Default(), Decor.Bold());
278 break;
280 case DL_Note:
281 Printf("%s note: %s", Decor.Black(), Decor.Default());
282 break;
285 renderText(Message, Args);
287 Printf("%s\n", Decor.Default());
289 if (Loc.isMemoryLocation())
290 renderMemorySnippet(Decor, Loc.getMemoryLocation(), Ranges,
291 NumRanges, Args);