10b57cec5SDimitry Andric //= OSLog.h - Analysis of calls to os_log builtins --*- C++ -*-===============//
20b57cec5SDimitry Andric //
30b57cec5SDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
40b57cec5SDimitry Andric // See https://llvm.org/LICENSE.txt for license information.
50b57cec5SDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
60b57cec5SDimitry Andric //
70b57cec5SDimitry Andric //===----------------------------------------------------------------------===//
80b57cec5SDimitry Andric //
90b57cec5SDimitry Andric // This file defines APIs for determining the layout of the data buffer for
100b57cec5SDimitry Andric // os_log() and os_trace().
110b57cec5SDimitry Andric //
120b57cec5SDimitry Andric //===----------------------------------------------------------------------===//
130b57cec5SDimitry Andric 
1404eeddc0SDimitry Andric #ifndef LLVM_CLANG_AST_OSLOG_H
1504eeddc0SDimitry Andric #define LLVM_CLANG_AST_OSLOG_H
160b57cec5SDimitry Andric 
170b57cec5SDimitry Andric #include "clang/AST/ASTContext.h"
180b57cec5SDimitry Andric #include "clang/AST/Expr.h"
190b57cec5SDimitry Andric 
200b57cec5SDimitry Andric namespace clang {
210b57cec5SDimitry Andric namespace analyze_os_log {
220b57cec5SDimitry Andric 
230b57cec5SDimitry Andric /// An OSLogBufferItem represents a single item in the data written by a call
240b57cec5SDimitry Andric /// to os_log() or os_trace().
250b57cec5SDimitry Andric class OSLogBufferItem {
260b57cec5SDimitry Andric public:
270b57cec5SDimitry Andric   enum Kind {
280b57cec5SDimitry Andric     // The item is a scalar (int, float, raw pointer, etc.). No further copying
290b57cec5SDimitry Andric     // is required. This is the only kind allowed by os_trace().
300b57cec5SDimitry Andric     ScalarKind = 0,
310b57cec5SDimitry Andric 
320b57cec5SDimitry Andric     // The item is a count, which describes the length of the following item to
330b57cec5SDimitry Andric     // be copied. A count may only be followed by an item of kind StringKind,
340b57cec5SDimitry Andric     // WideStringKind, or PointerKind.
350b57cec5SDimitry Andric     CountKind,
360b57cec5SDimitry Andric 
370b57cec5SDimitry Andric     // The item is a pointer to a C string. If preceded by a count 'n',
380b57cec5SDimitry Andric     // os_log() will copy at most 'n' bytes from the pointer.
390b57cec5SDimitry Andric     StringKind,
400b57cec5SDimitry Andric 
410b57cec5SDimitry Andric     // The item is a pointer to a block of raw data. This item must be preceded
420b57cec5SDimitry Andric     // by a count 'n'. os_log() will copy exactly 'n' bytes from the pointer.
430b57cec5SDimitry Andric     PointerKind,
440b57cec5SDimitry Andric 
450b57cec5SDimitry Andric     // The item is a pointer to an Objective-C object. os_log() may retain the
460b57cec5SDimitry Andric     // object for later processing.
470b57cec5SDimitry Andric     ObjCObjKind,
480b57cec5SDimitry Andric 
490b57cec5SDimitry Andric     // The item is a pointer to wide-char string.
500b57cec5SDimitry Andric     WideStringKind,
510b57cec5SDimitry Andric 
520b57cec5SDimitry Andric     // The item is corresponding to the '%m' format specifier, no value is
530b57cec5SDimitry Andric     // populated in the buffer and the runtime is loading the errno value.
540b57cec5SDimitry Andric     ErrnoKind,
550b57cec5SDimitry Andric 
560b57cec5SDimitry Andric     // The item is a mask type.
570b57cec5SDimitry Andric     MaskKind
580b57cec5SDimitry Andric   };
590b57cec5SDimitry Andric 
600b57cec5SDimitry Andric   enum {
610b57cec5SDimitry Andric     // The item is marked "private" in the format string.
620b57cec5SDimitry Andric     IsPrivate = 0x1,
630b57cec5SDimitry Andric 
640b57cec5SDimitry Andric     // The item is marked "public" in the format string.
650b57cec5SDimitry Andric     IsPublic = 0x2,
660b57cec5SDimitry Andric 
670b57cec5SDimitry Andric     // The item is marked "sensitive" in the format string.
680b57cec5SDimitry Andric     IsSensitive = 0x4 | IsPrivate
690b57cec5SDimitry Andric   };
700b57cec5SDimitry Andric 
710b57cec5SDimitry Andric private:
720b57cec5SDimitry Andric   Kind TheKind = ScalarKind;
730b57cec5SDimitry Andric   const Expr *TheExpr = nullptr;
740b57cec5SDimitry Andric   CharUnits ConstValue;
750b57cec5SDimitry Andric   CharUnits Size; // size of the data, not including the header bytes
760b57cec5SDimitry Andric   unsigned Flags = 0;
770b57cec5SDimitry Andric   StringRef MaskType;
780b57cec5SDimitry Andric 
790b57cec5SDimitry Andric public:
800b57cec5SDimitry Andric   OSLogBufferItem(Kind kind, const Expr *expr, CharUnits size, unsigned flags,
810b57cec5SDimitry Andric                   StringRef maskType = StringRef())
TheKind(kind)820b57cec5SDimitry Andric       : TheKind(kind), TheExpr(expr), Size(size), Flags(flags),
830b57cec5SDimitry Andric         MaskType(maskType) {
840b57cec5SDimitry Andric     assert(((Flags == 0) || (Flags == IsPrivate) || (Flags == IsPublic) ||
850b57cec5SDimitry Andric             (Flags == IsSensitive)) &&
860b57cec5SDimitry Andric            "unexpected privacy flag");
870b57cec5SDimitry Andric   }
880b57cec5SDimitry Andric 
OSLogBufferItem(ASTContext & Ctx,CharUnits value,unsigned flags)890b57cec5SDimitry Andric   OSLogBufferItem(ASTContext &Ctx, CharUnits value, unsigned flags)
900b57cec5SDimitry Andric       : TheKind(CountKind), ConstValue(value),
910b57cec5SDimitry Andric         Size(Ctx.getTypeSizeInChars(Ctx.IntTy)), Flags(flags) {}
920b57cec5SDimitry Andric 
getDescriptorByte()930b57cec5SDimitry Andric   unsigned char getDescriptorByte() const {
940b57cec5SDimitry Andric     unsigned char result = Flags;
950b57cec5SDimitry Andric     result |= ((unsigned)getKind()) << 4;
960b57cec5SDimitry Andric     return result;
970b57cec5SDimitry Andric   }
980b57cec5SDimitry Andric 
getSizeByte()990b57cec5SDimitry Andric   unsigned char getSizeByte() const { return size().getQuantity(); }
1000b57cec5SDimitry Andric 
getKind()1010b57cec5SDimitry Andric   Kind getKind() const { return TheKind; }
getIsPrivate()1020b57cec5SDimitry Andric   bool getIsPrivate() const { return (Flags & IsPrivate) != 0; }
1030b57cec5SDimitry Andric 
getExpr()1040b57cec5SDimitry Andric   const Expr *getExpr() const { return TheExpr; }
getConstValue()1050b57cec5SDimitry Andric   CharUnits getConstValue() const { return ConstValue; }
size()1060b57cec5SDimitry Andric   CharUnits size() const { return Size; }
1070b57cec5SDimitry Andric 
getMaskType()1080b57cec5SDimitry Andric   StringRef getMaskType() const { return MaskType; }
1090b57cec5SDimitry Andric };
1100b57cec5SDimitry Andric 
1110b57cec5SDimitry Andric class OSLogBufferLayout {
1120b57cec5SDimitry Andric public:
1130b57cec5SDimitry Andric   SmallVector<OSLogBufferItem, 4> Items;
1140b57cec5SDimitry Andric 
1150b57cec5SDimitry Andric   enum Flags { HasPrivateItems = 1, HasNonScalarItems = 1 << 1 };
1160b57cec5SDimitry Andric 
size()1170b57cec5SDimitry Andric   CharUnits size() const {
1180b57cec5SDimitry Andric     CharUnits result;
1190b57cec5SDimitry Andric     result += CharUnits::fromQuantity(2); // summary byte, num-args byte
1200b57cec5SDimitry Andric     for (auto &item : Items) {
1210b57cec5SDimitry Andric       // descriptor byte, size byte
1220b57cec5SDimitry Andric       result += item.size() + CharUnits::fromQuantity(2);
1230b57cec5SDimitry Andric     }
1240b57cec5SDimitry Andric     return result;
1250b57cec5SDimitry Andric   }
1260b57cec5SDimitry Andric 
hasPrivateItems()1270b57cec5SDimitry Andric   bool hasPrivateItems() const {
1280b57cec5SDimitry Andric     return llvm::any_of(
1290b57cec5SDimitry Andric         Items, [](const OSLogBufferItem &Item) { return Item.getIsPrivate(); });
1300b57cec5SDimitry Andric   }
1310b57cec5SDimitry Andric 
hasNonScalarOrMask()1320b57cec5SDimitry Andric   bool hasNonScalarOrMask() const {
1330b57cec5SDimitry Andric     return llvm::any_of(Items, [](const OSLogBufferItem &Item) {
1340b57cec5SDimitry Andric       return Item.getKind() != OSLogBufferItem::ScalarKind ||
1350b57cec5SDimitry Andric              !Item.getMaskType().empty();
1360b57cec5SDimitry Andric     });
1370b57cec5SDimitry Andric   }
1380b57cec5SDimitry Andric 
getSummaryByte()1390b57cec5SDimitry Andric   unsigned char getSummaryByte() const {
1400b57cec5SDimitry Andric     unsigned char result = 0;
1410b57cec5SDimitry Andric     if (hasPrivateItems())
1420b57cec5SDimitry Andric       result |= HasPrivateItems;
1430b57cec5SDimitry Andric     if (hasNonScalarOrMask())
1440b57cec5SDimitry Andric       result |= HasNonScalarItems;
1450b57cec5SDimitry Andric     return result;
1460b57cec5SDimitry Andric   }
1470b57cec5SDimitry Andric 
getNumArgsByte()1480b57cec5SDimitry Andric   unsigned char getNumArgsByte() const { return Items.size(); }
1490b57cec5SDimitry Andric };
1500b57cec5SDimitry Andric 
1510b57cec5SDimitry Andric // Given a call 'E' to one of the builtins __builtin_os_log_format() or
1520b57cec5SDimitry Andric // __builtin_os_log_format_buffer_size(), compute the layout of the buffer that
1530b57cec5SDimitry Andric // the call will write into and store it in 'layout'. Returns 'false' if there
1540b57cec5SDimitry Andric // was some error encountered while computing the layout, and 'true' otherwise.
1550b57cec5SDimitry Andric bool computeOSLogBufferLayout(clang::ASTContext &Ctx, const clang::CallExpr *E,
1560b57cec5SDimitry Andric                               OSLogBufferLayout &layout);
1570b57cec5SDimitry Andric 
1580b57cec5SDimitry Andric } // namespace analyze_os_log
1590b57cec5SDimitry Andric } // namespace clang
1600b57cec5SDimitry Andric #endif
161