Files
clang-p2996/lldb/tools/intel-features/intel-mpx/cli-wrapper-mpxtable.cpp
Abhishek Aggarwal 307db0f897 Tool for using Intel(R) Processor Trace hardware feature
Summary:
1. Provide single library for all Intel specific hardware features instead
    of individual libraries for each feature
2. Added Intel(R) Processor Trace hardware feature in this single library.
    Details about the tool implementing this feature is as follows:

     Tool developed on top of LLDB to provide its users the execution
     trace of the debugged inferiors. Tool's API are exposed as C++ object
     oriented interface in a shared library. API are designed especially to be
     easily integrable with IDEs providing LLDB as an application debugger.
     Entire API is also available as Python functions through a script bridging
     interface allowing development of python modules.

     This patch also provides a CLI wrapper to use the Tool through LLDB's command
     line. Highlights of the Tool and the wrapper are given below:

  ******************************
  Intel(R) Processor Trace Tool:
  ******************************
       - Provides execution trace of the debugged application
       - Uses Intel(R) Processor Trace hardware feature (already implemented inside LLDB)
         for this purpose
           -- Collects trace packets generated by this feature from LLDB, decodes and
              post-processes them
           -- Constructs the execution trace of the application
           -- Presents execution trace as a list of assembly instructions
       - Provides 4 APIs (exposed as C++ object oriented interface)
           -- start trace with configuration options for a thread/process,
           -- stop trace for a thread/process,
           -- get the execution flow (assembly instructions) for a thread,
           -- get trace specific information for a thread
       - Easily integrable into IDEs providing LLDB as application debugger
       - Entire API available as Python functions through script bridging interface
           -- Allows developing python apps on top of Tool
       - README_TOOL.txt provides more details about the Tool, its dependencies, building
         steps and API usage
       - Tool ready to use through LLDB's command line
           -- CLI wrapper has been developed on top of the Tool for this purpose

  *********************************
  CLI wrapper: cli-wrapper-pt.cpp
  *********************************
       - Provides 4 commands (syntax similar to LLDB's CLI commands):
           -- processor-trace start
           -- processor-trace stop
           -- processor-trace show-trace-options
           -- processor-trace show-instr-log
       - README_CLI.txt provides more details about commands and their options

Signed-off-by: Abhishek Aggarwal <abhishek.a.aggarwal@intel.com>

Reviewers: clayborg, jingham, lldb-commits, labath

Reviewed By: clayborg

Subscribers: ravitheja, emaste, krytarowski, mgorny

Differential Revision: https://reviews.llvm.org/D33035

llvm-svn: 310261
2017-08-07 15:26:11 +00:00

426 lines
13 KiB
C++

//===-- cli-wrapper-mpxtable.cpp----------------------------------*- C++
//-*-===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
// C++ includes
#include <cerrno>
#include <string>
// Project includes
#include "cli-wrapper-mpxtable.h"
#include "lldb/API/SBCommandInterpreter.h"
#include "lldb/API/SBCommandReturnObject.h"
#include "lldb/API/SBMemoryRegionInfo.h"
#include "lldb/API/SBProcess.h"
#include "lldb/API/SBTarget.h"
#include "lldb/API/SBThread.h"
#include "llvm/ADT/Triple.h"
static bool GetPtr(char *cptr, uint64_t &ptr, lldb::SBFrame &frame,
lldb::SBCommandReturnObject &result) {
if (!cptr) {
result.SetError("Bad argument.");
result.SetStatus(lldb::eReturnStatusFailed);
return false;
}
lldb::SBValue ptr_addr = frame.GetValueForVariablePath(cptr);
if (!ptr_addr.IsValid()) {
result.SetError("Invalid pointer.");
result.SetStatus(lldb::eReturnStatusFailed);
return false;
}
ptr = ptr_addr.GetLoadAddress();
return true;
}
enum {
mpx_base_mask_64 = ~(uint64_t)0xFFFULL,
mpx_bd_mask_64 = 0xFFFFFFF00000ULL,
bd_r_shift_64 = 20,
bd_l_shift_64 = 3,
bt_r_shift_64 = 3,
bt_l_shift_64 = 5,
bt_mask_64 = 0x0000000FFFF8ULL,
mpx_base_mask_32 = 0xFFFFFFFFFFFFF000ULL,
mpx_bd_mask_32 = 0xFFFFF000ULL,
bd_r_shift_32 = 12,
bd_l_shift_32 = 2,
bt_r_shift_32 = 2,
bt_l_shift_32 = 4,
bt_mask_32 = 0x00000FFCULL,
};
static void PrintBTEntry(lldb::addr_t lbound, lldb::addr_t ubound,
uint64_t value, uint64_t meta,
lldb::SBCommandReturnObject &result) {
const lldb::addr_t one_cmpl64 = ~((lldb::addr_t)0);
const lldb::addr_t one_cmpl32 = ~((uint32_t)0);
if ((lbound == one_cmpl64 || one_cmpl32) && ubound == 0) {
result.Printf("Null bounds on map: pointer value = 0x%lx\n", value);
} else {
result.Printf(" lbound = 0x%lx,", lbound);
result.Printf(" ubound = 0x%lx", ubound);
result.Printf(" (pointer value = 0x%lx,", value);
result.Printf(" metadata = 0x%lx)\n", meta);
}
}
static bool GetBTEntryAddr(uint64_t bndcfgu, uint64_t ptr,
lldb::SBTarget &target, llvm::Triple::ArchType arch,
size_t &size, lldb::addr_t &bt_entry_addr,
lldb::SBCommandReturnObject &result,
lldb::SBError &error) {
lldb::addr_t mpx_base_mask;
lldb::addr_t mpx_bd_mask;
lldb::addr_t bd_r_shift;
lldb::addr_t bd_l_shift;
lldb::addr_t bt_r_shift;
lldb::addr_t bt_l_shift;
lldb::addr_t bt_mask;
if (arch == llvm::Triple::ArchType::x86_64) {
mpx_base_mask = mpx_base_mask_64;
mpx_bd_mask = mpx_bd_mask_64;
bd_r_shift = bd_r_shift_64;
bd_l_shift = bd_l_shift_64;
bt_r_shift = bt_r_shift_64;
bt_l_shift = bt_l_shift_64;
bt_mask = bt_mask_64;
} else if (arch == llvm::Triple::ArchType::x86) {
mpx_base_mask = mpx_base_mask_32;
mpx_bd_mask = mpx_bd_mask_32;
bd_r_shift = bd_r_shift_32;
bd_l_shift = bd_l_shift_32;
bt_r_shift = bt_r_shift_32;
bt_l_shift = bt_l_shift_32;
bt_mask = bt_mask_32;
} else {
result.SetError("Invalid arch.");
result.SetStatus(lldb::eReturnStatusFailed);
return false;
}
size = target.GetAddressByteSize();
lldb::addr_t mpx_bd_base = bndcfgu & mpx_base_mask;
lldb::addr_t bd_entry_offset = ((ptr & mpx_bd_mask) >> bd_r_shift)
<< bd_l_shift;
lldb::addr_t bd_entry_addr = mpx_bd_base + bd_entry_offset;
std::vector<uint8_t> bd_entry_v(size);
size_t ret = target.GetProcess().ReadMemory(
bd_entry_addr, static_cast<void *>(bd_entry_v.data()), size, error);
if (ret != size || !error.Success()) {
result.SetError("Failed access to BD entry.");
return false;
}
lldb::SBData data;
data.SetData(error, bd_entry_v.data(), bd_entry_v.size(),
target.GetByteOrder(), size);
lldb::addr_t bd_entry = data.GetAddress(error, 0);
if (!error.Success()) {
result.SetError("Failed access to BD entry.");
return false;
}
if ((bd_entry & 0x01) == 0) {
result.SetError("Invalid bound directory.");
result.SetStatus(lldb::eReturnStatusFailed);
return false;
}
// Clear status bit.
//
bd_entry--;
lldb::addr_t bt_addr = bd_entry & ~bt_r_shift;
lldb::addr_t bt_entry_offset = ((ptr & bt_mask) >> bt_r_shift) << bt_l_shift;
bt_entry_addr = bt_addr + bt_entry_offset;
return true;
}
static bool GetBTEntry(uint64_t bndcfgu, uint64_t ptr, lldb::SBTarget &target,
llvm::Triple::ArchType arch,
lldb::SBCommandReturnObject &result,
lldb::SBError &error) {
lldb::addr_t bt_entry_addr;
size_t size;
if (!GetBTEntryAddr(bndcfgu, ptr, target, arch, size, bt_entry_addr, result,
error))
return false;
// bt_entry_v must have space to store the 4 elements of the BT entry (lower
// boundary,
// upper boundary, pointer value and meta data), which all have the same size
// 'size'.
//
std::vector<uint8_t> bt_entry_v(size * 4);
size_t ret = target.GetProcess().ReadMemory(
bt_entry_addr, static_cast<void *>(bt_entry_v.data()), size * 4, error);
if ((ret != (size * 4)) || !error.Success()) {
result.SetError("Unsuccessful. Failed access to BT entry.");
result.SetStatus(lldb::eReturnStatusFailed);
return false;
}
lldb::addr_t lbound;
lldb::addr_t ubound;
uint64_t value;
uint64_t meta;
lldb::SBData data;
data.SetData(error, bt_entry_v.data(), bt_entry_v.size(),
target.GetByteOrder(), size);
lbound = data.GetAddress(error, size * 0);
ubound = data.GetAddress(error, size * 1);
value = data.GetAddress(error, size * 2);
meta = data.GetAddress(error, size * 3);
// ubound is stored as one's complement.
if (arch == llvm::Triple::ArchType::x86) {
ubound = (~ubound) & 0x00000000FFFFFFFF;
} else {
ubound = ~ubound;
}
if (!error.Success()) {
result.SetError("Failed access to BT entry.");
return false;
}
PrintBTEntry(lbound, ubound, value, meta, result);
result.SetStatus(lldb::eReturnStatusSuccessFinishResult);
return true;
}
static std::vector<uint8_t> uIntToU8(uint64_t input, size_t size) {
std::vector<uint8_t> output;
for (size_t i = 0; i < size; i++)
output.push_back(
static_cast<uint8_t>((input & (0xFFULL << (i * 8))) >> (i * 8)));
return output;
}
static bool SetBTEntry(uint64_t bndcfgu, uint64_t ptr, lldb::addr_t lbound,
lldb::addr_t ubound, lldb::SBTarget &target,
llvm::Triple::ArchType arch,
lldb::SBCommandReturnObject &result,
lldb::SBError &error) {
lldb::addr_t bt_entry_addr;
size_t size;
if (!GetBTEntryAddr(bndcfgu, ptr, target, arch, size, bt_entry_addr, result,
error))
return false;
// bt_entry_v must have space to store only 2 elements of the BT Entry, the
// lower boundary and the upper boundary, which both have size 'size'.
//
std::vector<uint8_t> bt_entry_v(size * 2);
std::vector<uint8_t> lbound_v = uIntToU8(lbound, size);
bt_entry_v.insert(bt_entry_v.begin(), lbound_v.begin(), lbound_v.end());
std::vector<uint8_t> ubound_v = uIntToU8(~ubound, size);
bt_entry_v.insert(bt_entry_v.begin() + size, ubound_v.begin(),
ubound_v.end());
size_t ret = target.GetProcess().WriteMemory(
bt_entry_addr, (void *)(bt_entry_v.data()), size * 2, error);
if ((ret != (size * 2)) || !error.Success()) {
result.SetError("Failed access to BT entry.");
result.SetStatus(lldb::eReturnStatusFailed);
return false;
}
result.SetStatus(lldb::eReturnStatusSuccessFinishResult);
return true;
}
static bool GetInitInfo(lldb::SBDebugger debugger, lldb::SBTarget &target,
llvm::Triple::ArchType &arch, uint64_t &bndcfgu,
char *arg, uint64_t &ptr,
lldb::SBCommandReturnObject &result,
lldb::SBError &error) {
target = debugger.GetSelectedTarget();
if (!target.IsValid()) {
result.SetError("Invalid target.");
result.SetStatus(lldb::eReturnStatusFailed);
return false;
}
const std::string triple_s(target.GetTriple());
const llvm::Triple triple(triple_s);
arch = triple.getArch();
if ((arch != llvm::Triple::ArchType::x86) &&
(arch != llvm::Triple::ArchType::x86_64)) {
result.SetError("Platform not supported.");
result.SetStatus(lldb::eReturnStatusFailed);
return false;
}
lldb::SBFrame frame =
target.GetProcess().GetSelectedThread().GetSelectedFrame();
if (!frame.IsValid()) {
result.SetError("No valid process, thread or frame.");
result.SetStatus(lldb::eReturnStatusFailed);
return false;
}
lldb::SBValue bndcfgu_val = frame.FindRegister("bndcfgu");
if (!bndcfgu_val.IsValid()) {
result.SetError("Cannot access register BNDCFGU. Does the target support "
"Intel(R) Memory Protection Extensions (Intel(R) MPX)?");
result.SetStatus(lldb::eReturnStatusFailed);
return false;
}
lldb::SBData bndcfgu_data = bndcfgu_val.GetData();
bndcfgu = bndcfgu_data.GetUnsignedInt64(error, 0);
if (!error.Success()) {
result.SetError(error, "Invalid read of register BNDCFGU.");
return false;
}
if (!GetPtr(arg, ptr, frame, result))
return false;
return true;
}
class MPXTableShow : public lldb::SBCommandPluginInterface {
public:
virtual bool DoExecute(lldb::SBDebugger debugger, char **command,
lldb::SBCommandReturnObject &result) {
if (command) {
int arg_c = 0;
char *arg;
while (*command) {
if (arg_c >= 1) {
result.SetError("Too many arguments. See help.");
result.SetStatus(lldb::eReturnStatusFailed);
return false;
}
arg_c++;
arg = *command;
command++;
}
if (!debugger.IsValid()) {
result.SetError("Invalid debugger.");
result.SetStatus(lldb::eReturnStatusFailed);
return false;
}
lldb::SBTarget target;
llvm::Triple::ArchType arch;
lldb::SBError error;
uint64_t bndcfgu;
uint64_t ptr;
if (!GetInitInfo(debugger, target, arch, bndcfgu, arg, ptr, result,
error))
return false;
return GetBTEntry(bndcfgu, ptr, target, arch, result, error);
}
result.SetError("Too few arguments. See help.");
result.SetStatus(lldb::eReturnStatusFailed);
return false;
}
};
class MPXTableSet : public lldb::SBCommandPluginInterface {
public:
virtual bool DoExecute(lldb::SBDebugger debugger, char **command,
lldb::SBCommandReturnObject &result) {
if (command) {
int arg_c = 0;
char *arg[3];
while (*command) {
arg[arg_c] = *command;
command++;
arg_c++;
}
if (arg_c != 3) {
result.SetError("Wrong arguments. See help.");
return false;
}
if (!debugger.IsValid()) {
result.SetError("Invalid debugger.");
return false;
}
lldb::SBTarget target;
llvm::Triple::ArchType arch;
lldb::SBError error;
uint64_t bndcfgu;
uint64_t ptr;
if (!GetInitInfo(debugger, target, arch, bndcfgu, arg[0], ptr, result,
error))
return false;
char *endptr;
errno = 0;
uint64_t lbound = std::strtoul(arg[1], &endptr, 16);
if (endptr == arg[1] || errno == ERANGE) {
result.SetError("Lower Bound: bad argument format.");
errno = 0;
return false;
}
uint64_t ubound = std::strtoul(arg[2], &endptr, 16);
if (endptr == arg[1] || errno == ERANGE) {
result.SetError("Upper Bound: bad argument format.");
errno = 0;
return false;
}
return SetBTEntry(bndcfgu, ptr, lbound, ubound, target, arch, result,
error);
}
result.SetError("Too few arguments. See help.");
return false;
}
};
bool MPXPluginInitialize(lldb::SBDebugger &debugger) {
lldb::SBCommandInterpreter interpreter = debugger.GetCommandInterpreter();
lldb::SBCommand mpxTable = interpreter.AddMultiwordCommand(
"mpx-table", "A utility to access the Intel(R) MPX table entries.");
const char *mpx_show_help = "Show the Intel(R) MPX table entry of a pointer."
"\nmpx-table show <pointer>";
mpxTable.AddCommand("show", new MPXTableShow(), mpx_show_help);
const char *mpx_set_help =
"Set the Intel(R) MPX table entry of a pointer.\n"
"mpx-table set <pointer> <lower bound> <upper bound>";
mpxTable.AddCommand("set", new MPXTableSet(), mpx_set_help);
return true;
}