Skip to content

Commit

Permalink
[trace][intelpt] Support system-wide tracing [11] - Read warnings and…
Browse files Browse the repository at this point in the history
… perf conversion in the client

- Add logging for when the live state of the process is refreshed
- Move error handling of the live state refreshing to Trace from TraceIntelPT. This allows refreshing to fail either at the plug-in level or at the base class level. The error is cached and it can be gotten every time RefreshLiveProcessState is invoked.
- Allow DoRefreshLiveProcessState to handle plugin-specific parameters.
- Add some encapsulation to prevent TraceIntelPT from accessing variables belonging to Trace.

Test done via logging:

```
(lldb) b main
Breakpoint 1: where = a.out`main + 20 at main.cpp:27:20, address = 0x00000000004023d9
(lldb) r
Process 2359706 launched: '/home/wallace/a.out' (x86_64)
Process 2359706 stopped
* thread #1, name = 'a.out', stop reason = breakpoint 1.1
    frame #0: 0x00000000004023d9 a.out`main at main.cpp:27:20
   24   };
   25
   26   int main() {
-> 27     std::vector<int> vvv;
   28     for (int i = 0; i < 100000; i++)
   29       vvv.push_back(i);
   30
(lldb) process trace start                                                                                        (lldb) log enable lldb target -F(lldb) n
Process 2359706 stopped
* thread #1, name = 'a.out', stop reason = step over
    frame #0: 0x00000000004023e8 a.out`main at main.cpp:28:12
   25
   26   int main() {
   27     std::vector<int> vvv;
-> 28     for (int i = 0; i < 100000; i++)
   29       vvv.push_back(i);
   30
   31     std::deque<int> dq1 = {1, 2, 3};
(lldb) thread trace dump instructions -c 2 -t                                                                     Trace.cpp:RefreshLiveProcessState                            Trace::RefreshLiveProcessState invoked
TraceIntelPT.cpp:DoRefreshLiveProcessState                   TraceIntelPT found tsc conversion information
thread #1: tid = 2359706
  a.out`std::vector<int, std::allocator<int>>::vector() + 26 at stl_vector.h:395:19
    54: [tsc=unavailable] 0x0000000000403a7c    retq
```

See the logging lines at the end of the dump. They indicate that refreshing happened and that perf conversion information was found.

Differential Revision: https://reviews.llvm.org/D125943
  • Loading branch information
walter-erquinigo authored and memfrob committed Oct 5, 2022
1 parent d765d6a commit a6e13bf
Show file tree
Hide file tree
Showing 4 changed files with 83 additions and 41 deletions.
35 changes: 30 additions & 5 deletions lldb/include/lldb/Target/Trace.h
Original file line number Diff line number Diff line change
Expand Up @@ -265,6 +265,13 @@ class Trace : public PluginInterface,
llvm::Error OnThreadBinaryDataRead(lldb::tid_t tid, llvm::StringRef kind,
OnBinaryDataReadCallback callback);

/// Get the current traced live process.
///
/// \return
/// The current traced live process. If it's not a live process,
/// return \a nullptr.
Process *GetLiveProcess();

protected:
/// Implementation of \a OnThreadBinaryDataRead() for live threads.
llvm::Error OnLiveThreadBinaryDataRead(lldb::tid_t tid, llvm::StringRef kind,
Expand Down Expand Up @@ -365,14 +372,30 @@ class Trace : public PluginInterface,
///
/// \param[in] state
/// The jLLDBTraceGetState response.
virtual void
DoRefreshLiveProcessState(llvm::Expected<TraceGetStateResponse> state) = 0;
///
/// \param[in] json_response
/// The original JSON response as a string. It might be useful to redecode
/// it if it contains custom data for a specific trace plug-in.
///
/// \return
/// \b Error::success() if this operation succeedes, or an actual error
/// otherwise.
virtual llvm::Error
DoRefreshLiveProcessState(TraceGetStateResponse state,
llvm::StringRef json_response) = 0;

/// Method to be invoked by the plug-in to refresh the live process state.
/// Method to be invoked by the plug-in to refresh the live process state. It
/// will invoked DoRefreshLiveProcessState at some point, which should be
/// implemented by the plug-in for custom state handling.
///
/// The result is cached through the same process stop. Even in the case of
/// errors, it caches the error.
///
/// The result is cached through the same process stop.
void RefreshLiveProcessState();
/// \return
/// An error message if this operation failed, or \b nullptr otherwise.
const char *RefreshLiveProcessState();

private:
uint32_t m_stop_id = LLDB_INVALID_STOP_ID;
/// Process traced by this object if doing live tracing. Otherwise it's null.
Process *m_live_process = nullptr;
Expand All @@ -395,6 +418,8 @@ class Trace : public PluginInterface,
/// tid -> data kind -> file
llvm::DenseMap<lldb::tid_t, std::unordered_map<std::string, FileSpec>>
m_postmortem_thread_data;

llvm::Optional<std::string> m_live_refresh_error;
};

} // namespace lldb_private
Expand Down
33 changes: 19 additions & 14 deletions lldb/source/Plugins/Trace/intel-pt/TraceIntelPT.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -87,11 +87,10 @@ TraceIntelPT::TraceIntelPT(
}

DecodedThreadSP TraceIntelPT::Decode(Thread &thread) {
RefreshLiveProcessState();
if (m_live_refresh_error.hasValue())
if (const char *error = RefreshLiveProcessState())
return std::make_shared<DecodedThread>(
thread.shared_from_this(),
createStringError(inconvertibleErrorCode(), *m_live_refresh_error));
createStringError(inconvertibleErrorCode(), error));

auto it = m_thread_decoders.find(thread.GetID());
if (it == m_thread_decoders.end())
Expand Down Expand Up @@ -241,23 +240,29 @@ Expected<pt_cpu> TraceIntelPT::GetCPUInfo() {
return *m_cpu_info;
}

Process *TraceIntelPT::GetLiveProcess() { return m_live_process; }

void TraceIntelPT::DoRefreshLiveProcessState(
Expected<TraceGetStateResponse> state) {
Error TraceIntelPT::DoRefreshLiveProcessState(TraceGetStateResponse state,
StringRef json_response) {
m_thread_decoders.clear();

if (!state) {
m_live_refresh_error = toString(state.takeError());
return;
}

for (const TraceThreadState &thread_state : state->traced_threads) {
for (const TraceThreadState &thread_state : state.traced_threads) {
ThreadSP thread_sp =
m_live_process->GetThreadList().FindThreadByID(thread_state.tid);
GetLiveProcess()->GetThreadList().FindThreadByID(thread_state.tid);
m_thread_decoders.emplace(
thread_state.tid, std::make_unique<ThreadDecoder>(thread_sp, *this));
}

Expected<TraceIntelPTGetStateResponse> intelpt_state =
json::parse<TraceIntelPTGetStateResponse>(json_response,
"TraceIntelPTGetStateResponse");
if (!intelpt_state)
return intelpt_state.takeError();

m_tsc_conversion = intelpt_state->tsc_perf_zero_conversion;
if (m_tsc_conversion) {
Log *log = GetLog(LLDBLog::Target);
LLDB_LOG(log, "TraceIntelPT found TSC conversion information");
}
return Error::success();
}

bool TraceIntelPT::IsTraced(lldb::tid_t tid) {
Expand Down
16 changes: 6 additions & 10 deletions lldb/source/Plugins/Trace/intel-pt/TraceIntelPT.h
Original file line number Diff line number Diff line change
Expand Up @@ -75,8 +75,8 @@ class TraceIntelPT : public Trace {

llvm::Expected<size_t> GetRawTraceSize(Thread &thread);

void DoRefreshLiveProcessState(
llvm::Expected<TraceGetStateResponse> state) override;
llvm::Error DoRefreshLiveProcessState(TraceGetStateResponse state,
llvm::StringRef json_response) override;

bool IsTraced(lldb::tid_t tid) override;

Expand Down Expand Up @@ -148,12 +148,6 @@ class TraceIntelPT : public Trace {

llvm::Expected<pt_cpu> GetCPUInfo();

/// Get the current traced live process.
///
/// \return
/// The current traced live process. If it's not a live process,
/// return \a nullptr.
Process *GetLiveProcess();

/// \return
/// The timer object for this trace.
Expand Down Expand Up @@ -191,9 +185,11 @@ class TraceIntelPT : public Trace {
/// binary data.
llvm::Optional<pt_cpu> m_cpu_info;
std::map<lldb::tid_t, std::unique_ptr<ThreadDecoder>> m_thread_decoders;
/// Error gotten after a failed live process update, if any.
llvm::Optional<std::string> m_live_refresh_error;
/// Helper variable used to track long running operations for telemetry.
TaskTimer m_task_timer;
/// It is provided by either a session file or a live process to convert TSC
/// counters to and from nanos. It might not be available on all hosts.
llvm::Optional<LinuxPerfZeroTscConversion> m_tsc_conversion;
};

} // namespace trace_intel_pt
Expand Down
40 changes: 28 additions & 12 deletions lldb/source/Target/Trace.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
#include "lldb/Target/Process.h"
#include "lldb/Target/SectionLoadList.h"
#include "lldb/Target/Thread.h"
#include "lldb/Utility/LLDBLog.h"
#include "lldb/Utility/Stream.h"

using namespace lldb;
Expand Down Expand Up @@ -175,28 +176,37 @@ Trace::GetLiveProcessBinaryData(llvm::StringRef kind) {
return m_live_process->TraceGetBinaryData(request);
}

void Trace::RefreshLiveProcessState() {
const char *Trace::RefreshLiveProcessState() {
if (!m_live_process)
return;
return nullptr;

uint32_t new_stop_id = m_live_process->GetStopID();
if (new_stop_id == m_stop_id)
return;
return nullptr;

Log *log = GetLog(LLDBLog::Target);
LLDB_LOG(log, "Trace::RefreshLiveProcessState invoked");

m_stop_id = new_stop_id;
m_live_thread_data.clear();
m_live_refresh_error.reset();

auto HandleError = [&](Error &&err) -> const char * {
m_live_refresh_error = toString(std::move(err));
return m_live_refresh_error->c_str();
};

Expected<std::string> json_string = GetLiveProcessState();
if (!json_string) {
DoRefreshLiveProcessState(json_string.takeError());
return;
}
if (!json_string)
return HandleError(json_string.takeError());

Expected<TraceGetStateResponse> live_process_state =
json::parse<TraceGetStateResponse>(*json_string, "TraceGetStateResponse");
if (!live_process_state) {
DoRefreshLiveProcessState(live_process_state.takeError());
return;
}
if (!live_process_state)
return HandleError(live_process_state.takeError());

for (std::string &warning : live_process_state->warnings)
LLDB_LOG(log, "Warning when fetching the trace state: {0}", warning);

for (const TraceThreadState &thread_state :
live_process_state->traced_threads) {
Expand All @@ -207,9 +217,15 @@ void Trace::RefreshLiveProcessState() {
for (const TraceBinaryData &item : live_process_state->process_binary_data)
m_live_process_data[item.kind] = item.size;

DoRefreshLiveProcessState(std::move(live_process_state));
if (Error err = DoRefreshLiveProcessState(std::move(*live_process_state),
*json_string))
return HandleError(std::move(err));

return nullptr;
}

Process *Trace::GetLiveProcess() { return m_live_process; }

uint32_t Trace::GetStopID() {
RefreshLiveProcessState();
return m_stop_id;
Expand Down

0 comments on commit a6e13bf

Please sign in to comment.