diff options
Diffstat (limited to 'source/Utility')
-rw-r--r-- | source/Utility/CMakeLists.txt | 4 | ||||
-rw-r--r-- | source/Utility/Connection.cpp | 14 | ||||
-rw-r--r-- | source/Utility/IOObject.cpp | 15 | ||||
-rw-r--r-- | source/Utility/JSON.cpp | 2 | ||||
-rw-r--r-- | source/Utility/StructuredData.cpp | 286 | ||||
-rw-r--r-- | source/Utility/Timer.cpp | 132 |
6 files changed, 452 insertions, 1 deletions
diff --git a/source/Utility/CMakeLists.txt b/source/Utility/CMakeLists.txt index 31b14acda9629..78598562692bb 100644 --- a/source/Utility/CMakeLists.txt +++ b/source/Utility/CMakeLists.txt @@ -1,5 +1,6 @@ add_lldb_library(lldbUtility Baton.cpp + Connection.cpp ConstString.cpp DataBufferHeap.cpp DataBufferLLVM.cpp @@ -8,6 +9,7 @@ add_lldb_library(lldbUtility FastDemangle.cpp FileSpec.cpp History.cpp + IOObject.cpp JSON.cpp LLDBAssert.cpp Log.cpp @@ -26,8 +28,10 @@ add_lldb_library(lldbUtility StringExtractorGDBRemote.cpp StringLexer.cpp StringList.cpp + StructuredData.cpp TaskPool.cpp TildeExpressionResolver.cpp + Timer.cpp UserID.cpp UriParser.cpp UUID.cpp diff --git a/source/Utility/Connection.cpp b/source/Utility/Connection.cpp new file mode 100644 index 0000000000000..9f6114f6ed5de --- /dev/null +++ b/source/Utility/Connection.cpp @@ -0,0 +1,14 @@ +//===-- Connection.cpp ------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/Utility/Connection.h" + +using namespace lldb_private; + +Connection::~Connection() = default; diff --git a/source/Utility/IOObject.cpp b/source/Utility/IOObject.cpp new file mode 100644 index 0000000000000..df7929c4f9110 --- /dev/null +++ b/source/Utility/IOObject.cpp @@ -0,0 +1,15 @@ +//===-- IOObject.cpp --------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/Utility/IOObject.h" + +using namespace lldb_private; + +const IOObject::WaitableHandle IOObject::kInvalidHandleValue = -1; +IOObject::~IOObject() = default; diff --git a/source/Utility/JSON.cpp b/source/Utility/JSON.cpp index cb23f140cbfe0..1520bc7c47ec7 100644 --- a/source/Utility/JSON.cpp +++ b/source/Utility/JSON.cpp @@ -191,7 +191,7 @@ JSONValue::SP JSONArray::GetObject(Index i) { JSONArray::Size JSONArray::GetNumElements() { return m_elements.size(); } -JSONParser::JSONParser(const char *cstr) : StringExtractor(cstr) {} +JSONParser::JSONParser(llvm::StringRef data) : StringExtractor(data) {} JSONParser::Token JSONParser::GetToken(std::string &value) { StreamString error; diff --git a/source/Utility/StructuredData.cpp b/source/Utility/StructuredData.cpp new file mode 100644 index 0000000000000..9fc05354cfd3f --- /dev/null +++ b/source/Utility/StructuredData.cpp @@ -0,0 +1,286 @@ +//===---------------------StructuredData.cpp ---------------------*- C++-*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/Utility/StructuredData.h" +#include "lldb/Utility/DataBuffer.h" +#include "lldb/Utility/FileSpec.h" +#include "lldb/Utility/JSON.h" +#include "lldb/Utility/Status.h" +#include "lldb/Utility/Stream.h" // for Stream +#include "lldb/Utility/StreamString.h" +#include "llvm/ADT/STLExtras.h" // for make_unique +#include <cerrno> +#include <cstdlib> +#include <inttypes.h> +#include <limits> // for numeric_limits + +using namespace lldb_private; + +//---------------------------------------------------------------------- +// Functions that use a JSONParser to parse JSON into StructuredData +//---------------------------------------------------------------------- +static StructuredData::ObjectSP ParseJSONValue(JSONParser &json_parser); +static StructuredData::ObjectSP ParseJSONObject(JSONParser &json_parser); +static StructuredData::ObjectSP ParseJSONArray(JSONParser &json_parser); + +StructuredData::ObjectSP +StructuredData::ParseJSONFromFile(const FileSpec &input_spec, Status &error) { + StructuredData::ObjectSP return_sp; + if (!input_spec.Exists()) { + error.SetErrorStringWithFormatv("input file {0} does not exist.", + input_spec); + return return_sp; + } + + auto buffer_or_error = llvm::MemoryBuffer::getFile(input_spec.GetPath()); + if (!buffer_or_error) { + error.SetErrorStringWithFormatv("could not open input file: {0} - {1}.", + input_spec.GetPath(), + buffer_or_error.getError().message()); + return return_sp; + } + + JSONParser json_parser(buffer_or_error.get()->getBuffer()); + return_sp = ParseJSONValue(json_parser); + return return_sp; +} + +static StructuredData::ObjectSP ParseJSONObject(JSONParser &json_parser) { + // The "JSONParser::Token::ObjectStart" token should have already been + // consumed by the time this function is called + auto dict_up = llvm::make_unique<StructuredData::Dictionary>(); + + std::string value; + std::string key; + while (1) { + JSONParser::Token token = json_parser.GetToken(value); + + if (token == JSONParser::Token::String) { + key.swap(value); + token = json_parser.GetToken(value); + if (token == JSONParser::Token::Colon) { + StructuredData::ObjectSP value_sp = ParseJSONValue(json_parser); + if (value_sp) + dict_up->AddItem(key, value_sp); + else + break; + } + } else if (token == JSONParser::Token::ObjectEnd) { + return StructuredData::ObjectSP(dict_up.release()); + } else if (token == JSONParser::Token::Comma) { + continue; + } else { + break; + } + } + return StructuredData::ObjectSP(); +} + +static StructuredData::ObjectSP ParseJSONArray(JSONParser &json_parser) { + // The "JSONParser::Token::ObjectStart" token should have already been + // consumed + // by the time this function is called + auto array_up = llvm::make_unique<StructuredData::Array>(); + + std::string value; + std::string key; + while (1) { + StructuredData::ObjectSP value_sp = ParseJSONValue(json_parser); + if (value_sp) + array_up->AddItem(value_sp); + else + break; + + JSONParser::Token token = json_parser.GetToken(value); + if (token == JSONParser::Token::Comma) { + continue; + } else if (token == JSONParser::Token::ArrayEnd) { + return StructuredData::ObjectSP(array_up.release()); + } else { + break; + } + } + return StructuredData::ObjectSP(); +} + +static StructuredData::ObjectSP ParseJSONValue(JSONParser &json_parser) { + std::string value; + const JSONParser::Token token = json_parser.GetToken(value); + switch (token) { + case JSONParser::Token::ObjectStart: + return ParseJSONObject(json_parser); + + case JSONParser::Token::ArrayStart: + return ParseJSONArray(json_parser); + + case JSONParser::Token::Integer: { + uint64_t uval; + if (llvm::to_integer(value, uval, 0)) + return std::make_shared<StructuredData::Integer>(uval); + } break; + + case JSONParser::Token::Float: { + double val; + if (llvm::to_float(value, val)) + return std::make_shared<StructuredData::Float>(val); + } break; + + case JSONParser::Token::String: + return std::make_shared<StructuredData::String>(value); + + case JSONParser::Token::True: + case JSONParser::Token::False: + return std::make_shared<StructuredData::Boolean>(token == + JSONParser::Token::True); + + case JSONParser::Token::Null: + return std::make_shared<StructuredData::Null>(); + + default: + break; + } + return StructuredData::ObjectSP(); +} + +StructuredData::ObjectSP StructuredData::ParseJSON(std::string json_text) { + JSONParser json_parser(json_text.c_str()); + StructuredData::ObjectSP object_sp = ParseJSONValue(json_parser); + return object_sp; +} + +StructuredData::ObjectSP +StructuredData::Object::GetObjectForDotSeparatedPath(llvm::StringRef path) { + if (this->GetType() == lldb::eStructuredDataTypeDictionary) { + std::pair<llvm::StringRef, llvm::StringRef> match = path.split('.'); + std::string key = match.first.str(); + ObjectSP value = this->GetAsDictionary()->GetValueForKey(key); + if (value.get()) { + // Do we have additional words to descend? If not, return the + // value we're at right now. + if (match.second.empty()) { + return value; + } else { + return value->GetObjectForDotSeparatedPath(match.second); + } + } + return ObjectSP(); + } + + if (this->GetType() == lldb::eStructuredDataTypeArray) { + std::pair<llvm::StringRef, llvm::StringRef> match = path.split('['); + if (match.second.size() == 0) { + return this->shared_from_this(); + } + errno = 0; + uint64_t val = strtoul(match.second.str().c_str(), NULL, 10); + if (errno == 0) { + return this->GetAsArray()->GetItemAtIndex(val); + } + return ObjectSP(); + } + + return this->shared_from_this(); +} + +void StructuredData::Object::DumpToStdout(bool pretty_print) const { + StreamString stream; + Dump(stream, pretty_print); + llvm::outs() << stream.GetString(); +} + +void StructuredData::Array::Dump(Stream &s, bool pretty_print) const { + bool first = true; + s << "["; + if (pretty_print) { + s << "\n"; + s.IndentMore(); + } + for (const auto &item_sp : m_items) { + if (first) { + first = false; + } else { + s << ","; + if (pretty_print) + s << "\n"; + } + + if (pretty_print) + s.Indent(); + item_sp->Dump(s, pretty_print); + } + if (pretty_print) { + s.IndentLess(); + s.EOL(); + s.Indent(); + } + s << "]"; +} + +void StructuredData::Integer::Dump(Stream &s, bool pretty_print) const { + s.Printf("%" PRIu64, m_value); +} + +void StructuredData::Float::Dump(Stream &s, bool pretty_print) const { + s.Printf("%lg", m_value); +} + +void StructuredData::Boolean::Dump(Stream &s, bool pretty_print) const { + if (m_value == true) + s.PutCString("true"); + else + s.PutCString("false"); +} + +void StructuredData::String::Dump(Stream &s, bool pretty_print) const { + std::string quoted; + const size_t strsize = m_value.size(); + for (size_t i = 0; i < strsize; ++i) { + char ch = m_value[i]; + if (ch == '"' || ch == '\\') + quoted.push_back('\\'); + quoted.push_back(ch); + } + s.Printf("\"%s\"", quoted.c_str()); +} + +void StructuredData::Dictionary::Dump(Stream &s, bool pretty_print) const { + bool first = true; + s << "{"; + if (pretty_print) { + s << "\n"; + s.IndentMore(); + } + for (const auto &pair : m_dict) { + if (first) + first = false; + else { + s << ","; + if (pretty_print) + s << "\n"; + } + if (pretty_print) + s.Indent(); + s << "\"" << pair.first.AsCString() << "\" : "; + pair.second->Dump(s, pretty_print); + } + if (pretty_print) { + s.IndentLess(); + s.EOL(); + s.Indent(); + } + s << "}"; +} + +void StructuredData::Null::Dump(Stream &s, bool pretty_print) const { + s << "null"; +} + +void StructuredData::Generic::Dump(Stream &s, bool pretty_print) const { + s << "0x" << m_object; +} diff --git a/source/Utility/Timer.cpp b/source/Utility/Timer.cpp new file mode 100644 index 0000000000000..fe7787458fa6d --- /dev/null +++ b/source/Utility/Timer.cpp @@ -0,0 +1,132 @@ +//===-- Timer.cpp -----------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +#include "lldb/Utility/Timer.h" +#include "lldb/Utility/Stream.h" + +#include <algorithm> +#include <map> +#include <mutex> +#include <utility> // for pair +#include <vector> + +#include <assert.h> // for assert +#include <stdarg.h> // for va_end, va_list, va_start +#include <stdio.h> + +using namespace lldb_private; + +#define TIMER_INDENT_AMOUNT 2 + +namespace { +typedef std::vector<Timer *> TimerStack; +static std::atomic<Timer::Category *> g_categories; +} // end of anonymous namespace + +std::atomic<bool> Timer::g_quiet(true); +std::atomic<unsigned> Timer::g_display_depth(0); +static std::mutex &GetFileMutex() { + static std::mutex *g_file_mutex_ptr = new std::mutex(); + return *g_file_mutex_ptr; +} + +static TimerStack &GetTimerStackForCurrentThread() { + static thread_local TimerStack g_stack; + return g_stack; +} + +Timer::Category::Category(const char *cat) : m_name(cat) { + m_nanos.store(0, std::memory_order_release); + Category *expected = g_categories; + do { + m_next = expected; + } while (!g_categories.compare_exchange_weak(expected, this)); +} + +void Timer::SetQuiet(bool value) { g_quiet = value; } + +Timer::Timer(Timer::Category &category, const char *format, ...) + : m_category(category), m_total_start(std::chrono::steady_clock::now()) { + TimerStack &stack = GetTimerStackForCurrentThread(); + + stack.push_back(this); + if (g_quiet && stack.size() <= g_display_depth) { + std::lock_guard<std::mutex> lock(GetFileMutex()); + + // Indent + ::fprintf(stdout, "%*s", int(stack.size() - 1) * TIMER_INDENT_AMOUNT, ""); + // Print formatted string + va_list args; + va_start(args, format); + ::vfprintf(stdout, format, args); + va_end(args); + + // Newline + ::fprintf(stdout, "\n"); + } +} + +Timer::~Timer() { + using namespace std::chrono; + + auto stop_time = steady_clock::now(); + auto total_dur = stop_time - m_total_start; + auto timer_dur = total_dur - m_child_duration; + + TimerStack &stack = GetTimerStackForCurrentThread(); + if (g_quiet && stack.size() <= g_display_depth) { + std::lock_guard<std::mutex> lock(GetFileMutex()); + ::fprintf(stdout, "%*s%.9f sec (%.9f sec)\n", + int(stack.size() - 1) * TIMER_INDENT_AMOUNT, "", + duration<double>(total_dur).count(), + duration<double>(timer_dur).count()); + } + + assert(stack.back() == this); + stack.pop_back(); + if (!stack.empty()) + stack.back()->ChildDuration(total_dur); + + // Keep total results for each category so we can dump results. + m_category.m_nanos += std::chrono::nanoseconds(timer_dur).count(); +} + +void Timer::SetDisplayDepth(uint32_t depth) { g_display_depth = depth; } + +/* binary function predicate: + * - returns whether a person is less than another person + */ + +typedef std::pair<const char *, uint64_t> TimerEntry; + +static bool CategoryMapIteratorSortCriterion(const TimerEntry &lhs, + const TimerEntry &rhs) { + return lhs.second > rhs.second; +} + +void Timer::ResetCategoryTimes() { + for (Category *i = g_categories; i; i = i->m_next) + i->m_nanos.store(0, std::memory_order_release); +} + +void Timer::DumpCategoryTimes(Stream *s) { + std::vector<TimerEntry> sorted; + for (Category *i = g_categories; i; i = i->m_next) { + uint64_t nanos = i->m_nanos.load(std::memory_order_acquire); + if (nanos) + sorted.push_back(std::make_pair(i->m_name, nanos)); + } + if (sorted.empty()) + return; // Later code will break without any elements. + + // Sort by time + std::sort(sorted.begin(), sorted.end(), CategoryMapIteratorSortCriterion); + + for (const auto &timer : sorted) + s->Printf("%.9f sec for %s\n", timer.second / 1000000000., timer.first); +} |