diff options
author | Mehdi Amini <mehdi.amini@apple.com> | 2016-10-24 20:39:34 +0000 |
---|---|---|
committer | Mehdi Amini <mehdi.amini@apple.com> | 2016-10-24 20:39:34 +0000 |
commit | e753a979e8de8e4c3027d970798f00335ba0dbe5 (patch) | |
tree | 99b96423a675385030d91ab8db25d943d8f91b5a /lib/Analysis | |
parent | 4992f51254e86948e184416239d7267db6037075 (diff) | |
download | clang-e753a979e8de8e4c3027d970798f00335ba0dbe5.tar.gz |
Add support for __builtin_os_log_format[_buffer_size]
This reverts commit r285007 and reapply r284990, with a fix for the
opencl test that I broke. Original commit message follows:
These new builtins support a mechanism for logging OS events, using a
printf-like format string to specify the layout of data in a buffer.
The _buffer_size version of the builtin can be used to determine the size
of the buffer to allocate to hold the data, and then __builtin_os_log_format
can write data into that buffer. This implements format checking to report
mismatches between the format string and the data arguments. Most of this
code was written by Chris Willmore.
Differential Revision: https://reviews.llvm.org/D25888
git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@285019 91177308-0d34-0410-b5e6-96231b3b80d8
Diffstat (limited to 'lib/Analysis')
-rw-r--r-- | lib/Analysis/CMakeLists.txt | 1 | ||||
-rw-r--r-- | lib/Analysis/FormatString.cpp | 3 | ||||
-rw-r--r-- | lib/Analysis/OSLog.cpp | 177 | ||||
-rw-r--r-- | lib/Analysis/PrintfFormatString.cpp | 43 |
4 files changed, 222 insertions, 2 deletions
diff --git a/lib/Analysis/CMakeLists.txt b/lib/Analysis/CMakeLists.txt index 6c74c797d9..fdc9e6cee8 100644 --- a/lib/Analysis/CMakeLists.txt +++ b/lib/Analysis/CMakeLists.txt @@ -16,6 +16,7 @@ add_clang_library(clangAnalysis Dominators.cpp FormatString.cpp LiveVariables.cpp + OSLog.cpp ObjCNoReturn.cpp PostOrderCFGView.cpp PrintfFormatString.cpp diff --git a/lib/Analysis/FormatString.cpp b/lib/Analysis/FormatString.cpp index 2a518cac39..c62e537e92 100644 --- a/lib/Analysis/FormatString.cpp +++ b/lib/Analysis/FormatString.cpp @@ -591,6 +591,8 @@ const char *ConversionSpecifier::toString() const { case cArg: return "c"; case sArg: return "s"; case pArg: return "p"; + case PArg: + return "P"; case nArg: return "n"; case PercentArg: return "%"; case ScanListArg: return "["; @@ -866,6 +868,7 @@ bool FormatSpecifier::hasStandardConversionSpecifier( case ConversionSpecifier::ObjCObjArg: case ConversionSpecifier::ScanListArg: case ConversionSpecifier::PercentArg: + case ConversionSpecifier::PArg: return true; case ConversionSpecifier::CArg: case ConversionSpecifier::SArg: diff --git a/lib/Analysis/OSLog.cpp b/lib/Analysis/OSLog.cpp new file mode 100644 index 0000000000..b57e0d1d13 --- /dev/null +++ b/lib/Analysis/OSLog.cpp @@ -0,0 +1,177 @@ +// TODO: header template + +#include "clang/Analysis/Analyses/OSLog.h" +#include "clang/AST/Attr.h" +#include "clang/AST/Decl.h" +#include "clang/AST/DeclCXX.h" +#include "clang/AST/ExprObjC.h" +#include "clang/Analysis/Analyses/FormatString.h" +#include "clang/Basic/Builtins.h" +#include "llvm/ADT/SmallBitVector.h" + +using namespace clang; +using llvm::APInt; + +using clang::analyze_os_log::OSLogBufferItem; +using clang::analyze_os_log::OSLogBufferLayout; + +class OSLogFormatStringHandler + : public analyze_format_string::FormatStringHandler { +private: + struct ArgData { + const Expr *E = nullptr; + Optional<OSLogBufferItem::Kind> Kind; + Optional<unsigned> Size; + unsigned char Flags = 0; + }; + SmallVector<ArgData, 4> ArgsData; + ArrayRef<const Expr *> Args; + + OSLogBufferItem::Kind + getKind(analyze_format_string::ConversionSpecifier::Kind K) { + switch (K) { + case clang::analyze_format_string::ConversionSpecifier::sArg: // "%s" + return OSLogBufferItem::StringKind; + case clang::analyze_format_string::ConversionSpecifier::SArg: // "%S" + return OSLogBufferItem::WideStringKind; + case clang::analyze_format_string::ConversionSpecifier::PArg: { // "%P" + return OSLogBufferItem::PointerKind; + case clang::analyze_format_string::ConversionSpecifier::ObjCObjArg: // "%@" + return OSLogBufferItem::ObjCObjKind; + case clang::analyze_format_string::ConversionSpecifier::PrintErrno: // "%m" + return OSLogBufferItem::ErrnoKind; + default: + return OSLogBufferItem::ScalarKind; + } + } + } + +public: + OSLogFormatStringHandler(ArrayRef<const Expr *> Args) : Args(Args) { + ArgsData.reserve(Args.size()); + } + + virtual bool HandlePrintfSpecifier(const analyze_printf::PrintfSpecifier &FS, + const char *StartSpecifier, + unsigned SpecifierLen) { + if (!FS.consumesDataArgument() && + FS.getConversionSpecifier().getKind() != + clang::analyze_format_string::ConversionSpecifier::PrintErrno) + return false; + + ArgsData.emplace_back(); + unsigned ArgIndex = FS.getArgIndex(); + if (ArgIndex < Args.size()) + ArgsData.back().E = Args[ArgIndex]; + + // First get the Kind + ArgsData.back().Kind = getKind(FS.getConversionSpecifier().getKind()); + if (ArgsData.back().Kind != OSLogBufferItem::ErrnoKind && + !ArgsData.back().E) { + // missing argument + ArgsData.pop_back(); + return false; + } + + switch (FS.getConversionSpecifier().getKind()) { + case clang::analyze_format_string::ConversionSpecifier::sArg: // "%s" + case clang::analyze_format_string::ConversionSpecifier::SArg: { // "%S" + auto &precision = FS.getPrecision(); + switch (precision.getHowSpecified()) { + case clang::analyze_format_string::OptionalAmount::NotSpecified: // "%s" + break; + case clang::analyze_format_string::OptionalAmount::Constant: // "%.16s" + ArgsData.back().Size = precision.getConstantAmount(); + break; + case clang::analyze_format_string::OptionalAmount::Arg: // "%.*s" + ArgsData.back().Kind = OSLogBufferItem::CountKind; + break; + case clang::analyze_format_string::OptionalAmount::Invalid: + return false; + } + break; + } + case clang::analyze_format_string::ConversionSpecifier::PArg: { // "%P" + auto &precision = FS.getPrecision(); + switch (precision.getHowSpecified()) { + case clang::analyze_format_string::OptionalAmount::NotSpecified: // "%P" + return false; // length must be supplied with pointer format specifier + case clang::analyze_format_string::OptionalAmount::Constant: // "%.16P" + ArgsData.back().Size = precision.getConstantAmount(); + break; + case clang::analyze_format_string::OptionalAmount::Arg: // "%.*P" + ArgsData.back().Kind = OSLogBufferItem::CountKind; + break; + case clang::analyze_format_string::OptionalAmount::Invalid: + return false; + } + break; + } + default: + break; + } + + if (FS.isPrivate()) { + ArgsData.back().Flags |= OSLogBufferItem::IsPrivate; + } + if (FS.isPublic()) { + ArgsData.back().Flags |= OSLogBufferItem::IsPublic; + } + return true; + } + + void computeLayout(ASTContext &Ctx, OSLogBufferLayout &Layout) const { + Layout.Items.clear(); + for (auto &Data : ArgsData) { + if (Data.Size) + Layout.Items.emplace_back(Ctx, CharUnits::fromQuantity(*Data.Size), + Data.Flags); + if (Data.Kind) { + CharUnits Size; + if (*Data.Kind == OSLogBufferItem::ErrnoKind) + Size = CharUnits::Zero(); + else + Size = Ctx.getTypeSizeInChars(Data.E->getType()); + Layout.Items.emplace_back(*Data.Kind, Data.E, Size, Data.Flags); + } else { + auto Size = Ctx.getTypeSizeInChars(Data.E->getType()); + Layout.Items.emplace_back(OSLogBufferItem::ScalarKind, Data.E, Size, + Data.Flags); + } + } + } +}; + +bool clang::analyze_os_log::computeOSLogBufferLayout( + ASTContext &Ctx, const CallExpr *E, OSLogBufferLayout &Layout) { + ArrayRef<const Expr *> Args(E->getArgs(), E->getArgs() + E->getNumArgs()); + + const Expr *StringArg; + ArrayRef<const Expr *> VarArgs; + switch (E->getBuiltinCallee()) { + case Builtin::BI__builtin_os_log_format_buffer_size: + assert(E->getNumArgs() >= 1 && + "__builtin_os_log_format_buffer_size takes at least 1 argument"); + StringArg = E->getArg(0); + VarArgs = Args.slice(1); + break; + case Builtin::BI__builtin_os_log_format: + assert(E->getNumArgs() >= 2 && + "__builtin_os_log_format takes at least 2 arguments"); + StringArg = E->getArg(1); + VarArgs = Args.slice(2); + break; + default: + llvm_unreachable("non-os_log builtin passed to computeOSLogBufferLayout"); + } + + const StringLiteral *Lit = cast<StringLiteral>(StringArg->IgnoreParenCasts()); + assert(Lit && (Lit->isAscii() || Lit->isUTF8())); + StringRef Data = Lit->getString(); + OSLogFormatStringHandler H(VarArgs); + ParsePrintfString(H, Data.begin(), Data.end(), Ctx.getLangOpts(), + Ctx.getTargetInfo(), /*isFreeBSDKPrintf*/ false); + + H.computeLayout(Ctx, Layout); + return true; +} diff --git a/lib/Analysis/PrintfFormatString.cpp b/lib/Analysis/PrintfFormatString.cpp index ac6cef9d08..ed7193ecb4 100644 --- a/lib/Analysis/PrintfFormatString.cpp +++ b/lib/Analysis/PrintfFormatString.cpp @@ -119,6 +119,39 @@ static PrintfSpecifierResult ParsePrintfSpecifier(FormatStringHandler &H, return true; } + const char *OSLogVisibilityFlagsStart = nullptr, + *OSLogVisibilityFlagsEnd = nullptr; + if (*I == '{') { + OSLogVisibilityFlagsStart = I++; + // Find the end of the modifier. + while (I != E && *I != '}') { + I++; + } + if (I == E) { + if (Warn) + H.HandleIncompleteSpecifier(Start, E - Start); + return true; + } + assert(*I == '}'); + OSLogVisibilityFlagsEnd = I++; + + // Just see if 'private' or 'public' is the first word. os_log itself will + // do any further parsing. + const char *P = OSLogVisibilityFlagsStart + 1; + while (P < OSLogVisibilityFlagsEnd && isspace(*P)) + P++; + const char *WordStart = P; + while (P < OSLogVisibilityFlagsEnd && (isalnum(*P) || *P == '_')) + P++; + const char *WordEnd = P; + StringRef Word(WordStart, WordEnd - WordStart); + if (Word == "private") { + FS.setIsPrivate(WordStart); + } else if (Word == "public") { + FS.setIsPublic(WordStart); + } + } + // Look for flags (if any). bool hasMore = true; for ( ; I != E; ++I) { @@ -253,6 +286,10 @@ static PrintfSpecifierResult ParsePrintfSpecifier(FormatStringHandler &H, // POSIX specific. case 'C': k = ConversionSpecifier::CArg; break; case 'S': k = ConversionSpecifier::SArg; break; + // Apple extension for os_log + case 'P': + k = ConversionSpecifier::PArg; + break; // Objective-C. case '@': k = ConversionSpecifier::ObjCObjArg; break; // Glibc specific. @@ -301,7 +338,7 @@ static PrintfSpecifierResult ParsePrintfSpecifier(FormatStringHandler &H, conversionPosition); return true; } - + PrintfConversionSpecifier CS(conversionPosition, k); FS.setConversionSpecifier(CS); if (CS.consumesDataArgument() && !FS.usesPositionalArg()) @@ -541,6 +578,7 @@ ArgType PrintfSpecifier::getArgType(ASTContext &Ctx, return Ctx.IntTy; return ArgType(Ctx.WideCharTy, "wchar_t"); case ConversionSpecifier::pArg: + case ConversionSpecifier::PArg: return ArgType::CPointerTy; case ConversionSpecifier::ObjCObjArg: return ArgType::ObjCPointerTy; @@ -900,7 +938,7 @@ bool PrintfSpecifier::hasValidPrecision() const { if (Precision.getHowSpecified() == OptionalAmount::NotSpecified) return true; - // Precision is only valid with the diouxXaAeEfFgGs conversions + // Precision is only valid with the diouxXaAeEfFgGsP conversions switch (CS.getKind()) { case ConversionSpecifier::dArg: case ConversionSpecifier::DArg: @@ -922,6 +960,7 @@ bool PrintfSpecifier::hasValidPrecision() const { case ConversionSpecifier::sArg: case ConversionSpecifier::FreeBSDrArg: case ConversionSpecifier::FreeBSDyArg: + case ConversionSpecifier::PArg: return true; default: |