Skip to content

Commit bdf3e7e

Browse files
author
Walter Erquinigo
committed
[trace][intelpt] Add task timer classes
I'm adding two new classes that can be used to measure the duration of long tasks as process and thread level, e.g. decoding, fetching data from lldb-server, etc. In this first patch, I'm using it to measure the time it takes to decode each thread, which is printed out with the `dump info` command. In a later patch I'll start adding process-level tasks and I might move these classes to the upper Trace level, instead of having them in the intel-pt plugin. I might need to do that anyway in the future when we have to measure HTR. For now, I want to keep the impact of this change minimal. With it, I was able to generate the following info of a very big trace: ``` (lldb) thread trace dump info Trace technology: intel-pt thread #1: tid = 616081 Total number of instructions: 9729366 Memory usage: Raw trace size: 1024 KiB Total approximate memory usage (excluding raw trace): 123517.34 KiB Average memory usage per instruction (excluding raw trace): 13.00 bytes Timing: Decoding instructions: 1.62s Errors: Number of TSC decoding errors: 0 ``` As seen above, it took 1.62 seconds to decode 9.7M instructions. This is great news, as we don't need to do any optimization work in this area. Differential Revision: https://reviews.llvm.org/D123357
1 parent 9f52605 commit bdf3e7e

File tree

9 files changed

+163
-24
lines changed

9 files changed

+163
-24
lines changed

lldb/source/Plugins/Trace/intel-pt/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ lldb_tablegen(TraceIntelPTCommandOptions.inc -gen-lldb-option-defs
1616
add_lldb_library(lldbPluginTraceIntelPT PLUGIN
1717
CommandObjectTraceStartIntelPT.cpp
1818
DecodedThread.cpp
19+
TaskTimer.cpp
1920
LibiptDecoder.cpp
2021
ThreadDecoder.cpp
2122
TraceCursorIntelPT.cpp

lldb/source/Plugins/Trace/intel-pt/DecodedThread.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -239,6 +239,8 @@ class DecodedThread : public std::enable_shared_from_this<DecodedThread> {
239239
llvm::Optional<size_t> m_raw_trace_size;
240240
/// All occurrences of libipt errors when decoding TSCs.
241241
LibiptErrors m_tsc_errors;
242+
/// Total amount of time spent decoding.
243+
std::chrono::milliseconds m_total_decoding_time{0};
242244
};
243245

244246
using DecodedThreadSP = std::shared_ptr<DecodedThread>;
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
#include "TaskTimer.h"
2+
3+
using namespace lldb;
4+
using namespace lldb_private;
5+
using namespace lldb_private::trace_intel_pt;
6+
using namespace llvm;
7+
8+
void ThreadTaskTimer::ForEachTimedTask(
9+
std::function<void(const std::string &event,
10+
std::chrono::milliseconds duration)>
11+
callback) {
12+
for (const auto &kv : m_timed_tasks) {
13+
callback(kv.first, kv.second);
14+
}
15+
}
16+
17+
ThreadTaskTimer &TaskTimer::ForThread(lldb::tid_t tid) {
18+
auto it = m_thread_timers.find(tid);
19+
if (it == m_thread_timers.end())
20+
it = m_thread_timers.try_emplace(tid, ThreadTaskTimer{}).first;
21+
return it->second;
22+
}
Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
//===-- TaskTimer.h ---------------------------------------------*- C++ -*-===//
2+
//
3+
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4+
// See https://llvm.org/LICENSE.txt for license information.
5+
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6+
//
7+
//===----------------------------------------------------------------------===//
8+
9+
#ifndef LLDB_SOURCE_PLUGINS_TRACE_INTEL_PT_TASKTIMER_H
10+
#define LLDB_SOURCE_PLUGINS_TRACE_INTEL_PT_TASKTIMER_H
11+
12+
#include "lldb/lldb-types.h"
13+
14+
#include "llvm/ADT/DenseMap.h"
15+
#include "llvm/ADT/StringRef.h"
16+
17+
#include <chrono>
18+
#include <functional>
19+
#include <unordered_map>
20+
21+
namespace lldb_private {
22+
namespace trace_intel_pt {
23+
24+
/// Class used to track the duration of long running tasks related to a single
25+
/// thread for reporting.
26+
class ThreadTaskTimer {
27+
public:
28+
/// Execute the given \p task and record its duration.
29+
///
30+
/// \param[in] name
31+
/// The name used to identify this task for reporting.
32+
///
33+
/// \param[in] task
34+
/// The task function.
35+
///
36+
/// \return
37+
/// The return value of the task.
38+
template <class R> R TimeTask(llvm::StringRef name, std::function<R()> task) {
39+
auto start = std::chrono::steady_clock::now();
40+
R result = task();
41+
auto end = std::chrono::steady_clock::now();
42+
std::chrono::milliseconds duration =
43+
std::chrono::duration_cast<std::chrono::milliseconds>(end - start);
44+
m_timed_tasks.insert({name.str(), duration});
45+
return result;
46+
}
47+
48+
/// Executive the given \p callback on each recorded task.
49+
///
50+
/// \param[in] callback
51+
/// The first parameter of the callback is the name of the recorded task,
52+
/// and the second parameter is the duration of that task.
53+
void ForEachTimedTask(std::function<void(const std::string &name,
54+
std::chrono::milliseconds duration)>
55+
callback);
56+
57+
private:
58+
std::unordered_map<std::string, std::chrono::milliseconds> m_timed_tasks;
59+
};
60+
61+
/// Class used to track the duration of long running tasks for reporting.
62+
class TaskTimer {
63+
public:
64+
/// \return
65+
/// The timer object for the given thread.
66+
ThreadTaskTimer &ForThread(lldb::tid_t tid);
67+
68+
private:
69+
llvm::DenseMap<lldb::tid_t, ThreadTaskTimer> m_thread_timers;
70+
};
71+
72+
} // namespace trace_intel_pt
73+
} // namespace lldb_private
74+
75+
#endif // LLDB_SOURCE_PLUGINS_TRACE_INTEL_PT_TASKTIMER_H

lldb/source/Plugins/Trace/intel-pt/ThreadDecoder.cpp

Lines changed: 15 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -30,15 +30,20 @@ DecodedThreadSP ThreadDecoder::Decode() {
3030
}
3131

3232
DecodedThreadSP ThreadDecoder::DoDecode() {
33-
DecodedThreadSP decoded_thread_sp =
34-
std::make_shared<DecodedThread>(m_thread_sp);
35-
36-
Error err = m_trace.OnThreadBufferRead(
37-
m_thread_sp->GetID(), [&](llvm::ArrayRef<uint8_t> data) {
38-
DecodeTrace(*decoded_thread_sp, m_trace, data);
39-
return Error::success();
33+
return m_trace.GetTimer()
34+
.ForThread(m_thread_sp->GetID())
35+
.TimeTask<DecodedThreadSP>("Decoding instructions", [&]() {
36+
DecodedThreadSP decoded_thread_sp =
37+
std::make_shared<DecodedThread>(m_thread_sp);
38+
39+
Error err = m_trace.OnThreadBufferRead(
40+
m_thread_sp->GetID(), [&](llvm::ArrayRef<uint8_t> data) {
41+
DecodeTrace(*decoded_thread_sp, m_trace, data);
42+
return Error::success();
43+
});
44+
45+
if (err)
46+
decoded_thread_sp->AppendError(std::move(err));
47+
return decoded_thread_sp;
4048
});
41-
if (err)
42-
decoded_thread_sp->AppendError(std::move(err));
43-
return decoded_thread_sp;
4449
}

lldb/source/Plugins/Trace/intel-pt/TraceIntelPT.cpp

Lines changed: 21 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -117,19 +117,32 @@ void TraceIntelPT::DumpTraceInfo(Thread &thread, Stream &s, bool verbose) {
117117
size_t insn_len = decoded_trace_sp->GetInstructionsCount();
118118
size_t mem_used = decoded_trace_sp->CalculateApproximateMemoryUsage();
119119

120-
s.Format(" Raw trace size: {0} KiB\n", *raw_size / 1024);
121120
s.Format(" Total number of instructions: {0}\n", insn_len);
122-
s.Format(" Total approximate memory usage: {0:2} KiB\n",
123-
(double)mem_used / 1024);
121+
122+
s.PutCString("\n Memory usage:\n");
123+
s.Format(" Raw trace size: {0} KiB\n", *raw_size / 1024);
124+
s.Format(
125+
" Total approximate memory usage (excluding raw trace): {0:2} KiB\n",
126+
(double)mem_used / 1024);
124127
if (insn_len != 0)
125-
s.Format(" Average memory usage per instruction: {0:2} bytes\n",
128+
s.Format(" Average memory usage per instruction (excluding raw trace): "
129+
"{0:2} bytes\n",
126130
(double)mem_used / insn_len);
127131

132+
s.PutCString("\n Timing:\n");
133+
GetTimer()
134+
.ForThread(thread.GetID())
135+
.ForEachTimedTask(
136+
[&](const std::string &name, std::chrono::milliseconds duration) {
137+
s.Format(" {0}: {1:2}s\n", name, duration.count() / 1000.0);
138+
});
139+
140+
s.PutCString("\n Errors:\n");
128141
const DecodedThread::LibiptErrors &tsc_errors =
129142
decoded_trace_sp->GetTscErrors();
130-
s.Format("\n Number of TSC decoding errors: {0}\n", tsc_errors.total_count);
143+
s.Format(" Number of TSC decoding errors: {0}\n", tsc_errors.total_count);
131144
for (const auto &error_message_to_count : tsc_errors.libipt_errors) {
132-
s.Format(" {0}: {1}\n", error_message_to_count.first,
145+
s.Format(" {0}: {1}\n", error_message_to_count.first,
133146
error_message_to_count.second);
134147
}
135148
}
@@ -358,3 +371,5 @@ Error TraceIntelPT::OnThreadBufferRead(lldb::tid_t tid,
358371
OnBinaryDataReadCallback callback) {
359372
return OnThreadBinaryDataRead(tid, "threadTraceBuffer", callback);
360373
}
374+
375+
TaskTimer &TraceIntelPT::GetTimer() { return m_task_timer; }

lldb/source/Plugins/Trace/intel-pt/TraceIntelPT.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
#ifndef LLDB_SOURCE_PLUGINS_TRACE_INTEL_PT_TRACEINTELPT_H
1010
#define LLDB_SOURCE_PLUGINS_TRACE_INTEL_PT_TRACEINTELPT_H
1111

12+
#include "TaskTimer.h"
1213
#include "ThreadDecoder.h"
1314
#include "TraceIntelPTSessionFileParser.h"
1415
#include "lldb/Utility/FileSpec.h"
@@ -150,6 +151,10 @@ class TraceIntelPT : public Trace {
150151
/// return \a nullptr.
151152
Process *GetLiveProcess();
152153

154+
/// \return
155+
/// The timer object for this trace.
156+
TaskTimer &GetTimer();
157+
153158
private:
154159
friend class TraceIntelPTSessionFileParser;
155160

@@ -184,6 +189,7 @@ class TraceIntelPT : public Trace {
184189
std::map<lldb::tid_t, std::unique_ptr<ThreadDecoder>> m_thread_decoders;
185190
/// Error gotten after a failed live process update, if any.
186191
llvm::Optional<std::string> m_live_refresh_error;
192+
TaskTimer m_task_timer;
187193
};
188194

189195
} // namespace trace_intel_pt

lldb/test/API/commands/trace/TestTraceDumpInfo.py

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -38,9 +38,16 @@ def testDumpRawTraceSize(self):
3838
substrs=['''Trace technology: intel-pt
3939
4040
thread #1: tid = 3842849
41-
Raw trace size: 4 KiB
4241
Total number of instructions: 21
43-
Total approximate memory usage: 0.27 KiB
44-
Average memory usage per instruction: 13.00 bytes
4542
46-
Number of TSC decoding errors: 0'''])
43+
Memory usage:
44+
Raw trace size: 4 KiB
45+
Total approximate memory usage (excluding raw trace): 0.27 KiB
46+
Average memory usage per instruction (excluding raw trace): 13.00 bytes
47+
48+
Timing:
49+
Decoding instructions: ''', '''s
50+
51+
Errors:
52+
Number of TSC decoding errors: 0'''],
53+
patterns=["Decoding instructions: \d.\d\ds"])

lldb/test/API/commands/trace/TestTraceLoad.py

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -36,12 +36,18 @@ def testLoadTrace(self):
3636
self.expect("thread trace dump info", substrs=['''Trace technology: intel-pt
3737
3838
thread #1: tid = 3842849
39-
Raw trace size: 4 KiB
4039
Total number of instructions: 21
41-
Total approximate memory usage: 0.27 KiB
42-
Average memory usage per instruction: 13.00 bytes
4340
44-
Number of TSC decoding errors: 0'''])
41+
Memory usage:
42+
Raw trace size: 4 KiB
43+
Total approximate memory usage (excluding raw trace): 0.27 KiB
44+
Average memory usage per instruction (excluding raw trace): 13.00 bytes
45+
46+
Timing:
47+
Decoding instructions: ''', '''s
48+
49+
Errors:
50+
Number of TSC decoding errors: 0'''])
4551

4652
def testLoadInvalidTraces(self):
4753
src_dir = self.getSourceDir()

0 commit comments

Comments
 (0)