* include/bits/allocator.h (operator==, operator!=): Add exception
[official-gcc.git] / libsanitizer / ubsan / ubsan_diag.cc
blob786ffa7254f5ba9fc7887f3b0e6960c1b6b87bf1
1 //===-- ubsan_diag.cc -----------------------------------------------------===//
2 //
3 // This file is distributed under the University of Illinois Open Source
4 // License. See LICENSE.TXT for details.
5 //
6 //===----------------------------------------------------------------------===//
7 //
8 // Diagnostic reporting for the UBSan runtime.
9 //
10 //===----------------------------------------------------------------------===//
12 #include "ubsan_diag.h"
13 #include "sanitizer_common/sanitizer_common.h"
14 #include "sanitizer_common/sanitizer_libc.h"
15 #include "sanitizer_common/sanitizer_report_decorator.h"
16 #include "sanitizer_common/sanitizer_stacktrace.h"
17 #include "sanitizer_common/sanitizer_symbolizer.h"
18 #include <stdio.h>
20 using namespace __ubsan;
22 Location __ubsan::getCallerLocation(uptr CallerLoc) {
23 if (!CallerLoc)
24 return Location();
26 uptr Loc = StackTrace::GetPreviousInstructionPc(CallerLoc);
27 return getFunctionLocation(Loc, 0);
30 Location __ubsan::getFunctionLocation(uptr Loc, const char **FName) {
31 if (!Loc)
32 return Location();
34 AddressInfo Info;
35 if (!Symbolizer::GetOrInit()->SymbolizeCode(Loc, &Info, 1) ||
36 !Info.module || !*Info.module)
37 return Location(Loc);
39 if (FName && Info.function)
40 *FName = Info.function;
42 if (!Info.file)
43 return ModuleLocation(Info.module, Info.module_offset);
45 return SourceLocation(Info.file, Info.line, Info.column);
48 Diag &Diag::operator<<(const TypeDescriptor &V) {
49 return AddArg(V.getTypeName());
52 Diag &Diag::operator<<(const Value &V) {
53 if (V.getType().isSignedIntegerTy())
54 AddArg(V.getSIntValue());
55 else if (V.getType().isUnsignedIntegerTy())
56 AddArg(V.getUIntValue());
57 else if (V.getType().isFloatTy())
58 AddArg(V.getFloatValue());
59 else
60 AddArg("<unknown>");
61 return *this;
64 /// Hexadecimal printing for numbers too large for Printf to handle directly.
65 static void PrintHex(UIntMax Val) {
66 #if HAVE_INT128_T
67 Printf("0x%08x%08x%08x%08x",
68 (unsigned int)(Val >> 96),
69 (unsigned int)(Val >> 64),
70 (unsigned int)(Val >> 32),
71 (unsigned int)(Val));
72 #else
73 UNREACHABLE("long long smaller than 64 bits?");
74 #endif
77 static void renderLocation(Location Loc) {
78 InternalScopedString LocBuffer(1024);
79 switch (Loc.getKind()) {
80 case Location::LK_Source: {
81 SourceLocation SLoc = Loc.getSourceLocation();
82 if (SLoc.isInvalid())
83 LocBuffer.append("<unknown>");
84 else
85 PrintSourceLocation(&LocBuffer, SLoc.getFilename(), SLoc.getLine(),
86 SLoc.getColumn());
87 break;
89 case Location::LK_Module:
90 PrintModuleAndOffset(&LocBuffer, Loc.getModuleLocation().getModuleName(),
91 Loc.getModuleLocation().getOffset());
92 break;
93 case Location::LK_Memory:
94 LocBuffer.append("%p", Loc.getMemoryLocation());
95 break;
96 case Location::LK_Null:
97 LocBuffer.append("<unknown>");
98 break;
100 Printf("%s:", LocBuffer.data());
103 static void renderText(const char *Message, const Diag::Arg *Args) {
104 for (const char *Msg = Message; *Msg; ++Msg) {
105 if (*Msg != '%') {
106 char Buffer[64];
107 unsigned I;
108 for (I = 0; Msg[I] && Msg[I] != '%' && I != 63; ++I)
109 Buffer[I] = Msg[I];
110 Buffer[I] = '\0';
111 Printf(Buffer);
112 Msg += I - 1;
113 } else {
114 const Diag::Arg &A = Args[*++Msg - '0'];
115 switch (A.Kind) {
116 case Diag::AK_String:
117 Printf("%s", A.String);
118 break;
119 case Diag::AK_Mangled: {
120 Printf("'%s'", Symbolizer::GetOrInit()->Demangle(A.String));
121 break;
123 case Diag::AK_SInt:
124 // 'long long' is guaranteed to be at least 64 bits wide.
125 if (A.SInt >= INT64_MIN && A.SInt <= INT64_MAX)
126 Printf("%lld", (long long)A.SInt);
127 else
128 PrintHex(A.SInt);
129 break;
130 case Diag::AK_UInt:
131 if (A.UInt <= UINT64_MAX)
132 Printf("%llu", (unsigned long long)A.UInt);
133 else
134 PrintHex(A.UInt);
135 break;
136 case Diag::AK_Float: {
137 // FIXME: Support floating-point formatting in sanitizer_common's
138 // printf, and stop using snprintf here.
139 char Buffer[32];
140 snprintf(Buffer, sizeof(Buffer), "%Lg", (long double)A.Float);
141 Printf("%s", Buffer);
142 break;
144 case Diag::AK_Pointer:
145 Printf("%p", A.Pointer);
146 break;
152 /// Find the earliest-starting range in Ranges which ends after Loc.
153 static Range *upperBound(MemoryLocation Loc, Range *Ranges,
154 unsigned NumRanges) {
155 Range *Best = 0;
156 for (unsigned I = 0; I != NumRanges; ++I)
157 if (Ranges[I].getEnd().getMemoryLocation() > Loc &&
158 (!Best ||
159 Best->getStart().getMemoryLocation() >
160 Ranges[I].getStart().getMemoryLocation()))
161 Best = &Ranges[I];
162 return Best;
165 /// Render a snippet of the address space near a location.
166 static void renderMemorySnippet(const __sanitizer::AnsiColorDecorator &Decor,
167 MemoryLocation Loc,
168 Range *Ranges, unsigned NumRanges,
169 const Diag::Arg *Args) {
170 const unsigned BytesToShow = 32;
171 const unsigned MinBytesNearLoc = 4;
173 // Show at least the 8 bytes surrounding Loc.
174 MemoryLocation Min = Loc - MinBytesNearLoc, Max = Loc + MinBytesNearLoc;
175 for (unsigned I = 0; I < NumRanges; ++I) {
176 Min = __sanitizer::Min(Ranges[I].getStart().getMemoryLocation(), Min);
177 Max = __sanitizer::Max(Ranges[I].getEnd().getMemoryLocation(), Max);
180 // If we have too many interesting bytes, prefer to show bytes after Loc.
181 if (Max - Min > BytesToShow)
182 Min = __sanitizer::Min(Max - BytesToShow, Loc - MinBytesNearLoc);
183 Max = Min + BytesToShow;
185 // Emit data.
186 for (uptr P = Min; P != Max; ++P) {
187 // FIXME: Check that the address is readable before printing it.
188 unsigned char C = *reinterpret_cast<const unsigned char*>(P);
189 Printf("%s%02x", (P % 8 == 0) ? " " : " ", C);
191 Printf("\n");
193 // Emit highlights.
194 Printf(Decor.Green());
195 Range *InRange = upperBound(Min, Ranges, NumRanges);
196 for (uptr P = Min; P != Max; ++P) {
197 char Pad = ' ', Byte = ' ';
198 if (InRange && InRange->getEnd().getMemoryLocation() == P)
199 InRange = upperBound(P, Ranges, NumRanges);
200 if (!InRange && P > Loc)
201 break;
202 if (InRange && InRange->getStart().getMemoryLocation() < P)
203 Pad = '~';
204 if (InRange && InRange->getStart().getMemoryLocation() <= P)
205 Byte = '~';
206 char Buffer[] = { Pad, Pad, P == Loc ? '^' : Byte, Byte, 0 };
207 Printf((P % 8 == 0) ? Buffer : &Buffer[1]);
209 Printf("%s\n", Decor.Default());
211 // Go over the line again, and print names for the ranges.
212 InRange = 0;
213 unsigned Spaces = 0;
214 for (uptr P = Min; P != Max; ++P) {
215 if (!InRange || InRange->getEnd().getMemoryLocation() == P)
216 InRange = upperBound(P, Ranges, NumRanges);
217 if (!InRange)
218 break;
220 Spaces += (P % 8) == 0 ? 2 : 1;
222 if (InRange && InRange->getStart().getMemoryLocation() == P) {
223 while (Spaces--)
224 Printf(" ");
225 renderText(InRange->getText(), Args);
226 Printf("\n");
227 // FIXME: We only support naming one range for now!
228 break;
231 Spaces += 2;
234 // FIXME: Print names for anything we can identify within the line:
236 // * If we can identify the memory itself as belonging to a particular
237 // global, stack variable, or dynamic allocation, then do so.
239 // * If we have a pointer-size, pointer-aligned range highlighted,
240 // determine whether the value of that range is a pointer to an
241 // entity which we can name, and if so, print that name.
243 // This needs an external symbolizer, or (preferably) ASan instrumentation.
246 Diag::~Diag() {
247 __sanitizer::AnsiColorDecorator Decor(PrintsToTty());
248 SpinMutexLock l(&CommonSanitizerReportMutex);
249 Printf(Decor.Bold());
251 renderLocation(Loc);
253 switch (Level) {
254 case DL_Error:
255 Printf("%s runtime error: %s%s",
256 Decor.Red(), Decor.Default(), Decor.Bold());
257 break;
259 case DL_Note:
260 Printf("%s note: %s", Decor.Black(), Decor.Default());
261 break;
264 renderText(Message, Args);
266 Printf("%s\n", Decor.Default());
268 if (Loc.isMemoryLocation())
269 renderMemorySnippet(Decor, Loc.getMemoryLocation(), Ranges,
270 NumRanges, Args);