-
Notifications
You must be signed in to change notification settings - Fork 14.3k
[lldb][progress][NFC] Add groundwork to keep track of progress reports #81026
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change | ||||
---|---|---|---|---|---|---|
|
@@ -12,6 +12,7 @@ | |||||
#include "lldb/Utility/ConstString.h" | ||||||
#include "lldb/lldb-types.h" | ||||||
#include <atomic> | ||||||
#include <map> | ||||||
#include <mutex> | ||||||
#include <optional> | ||||||
|
||||||
|
@@ -55,6 +56,10 @@ namespace lldb_private { | |||||
|
||||||
class Progress { | ||||||
public: | ||||||
enum { | ||||||
eProgressLinearReports, | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Please comment what each enum value means |
||||||
eProgressCoalecseReports, | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||
}; | ||||||
Comment on lines
+59
to
+62
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Should we add a new setting for this? Something like:
|
||||||
/// Construct a progress object that will report information. | ||||||
/// | ||||||
/// The constructor will create a unique progress reporting object and | ||||||
|
@@ -71,7 +76,8 @@ class Progress { | |||||
/// progress is to be reported only to specific debuggers. | ||||||
Progress(std::string title, std::string details = {}, | ||||||
std::optional<uint64_t> total = std::nullopt, | ||||||
lldb_private::Debugger *debugger = nullptr); | ||||||
lldb_private::Debugger *debugger = nullptr, | ||||||
bool type = Progress::eProgressLinearReports); | ||||||
|
||||||
/// Destroy the progress object. | ||||||
/// | ||||||
|
@@ -99,6 +105,10 @@ class Progress { | |||||
private: | ||||||
void ReportProgress(); | ||||||
static std::atomic<uint64_t> g_id; | ||||||
static std::atomic<uint64_t> g_refcount; | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. By making the refcount static, it's shared across all the |
||||||
/// Map that tracks each progress object and if we've seen its start and stop | ||||||
/// events | ||||||
static std::unordered_map<std::string, uint64_t> g_map; | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'd move this to be tied to the debugger instance instead of making it static here. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We spoke about this a little offline but at the moment although Progress reports hold on to debugger instances, these instances are null by default and most Progress reports don't give a debugger instance in their instantiation so we wouldn't be able to have a debugger instance hold on to their own map of ongoing progress reports. @clayborg Having a progress report holding on to a debugger instance is a good idea, but if we want to use a map to keep track of ongoing reports we need to know what will own the map? Do we want the map to be owned by a debugger instance or could the progress class hold on to its own map of ongoing reports? In the case of the former we would then want to enforce that progress reports have valid debugger instances and in the latter we could remove the There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
The main question is if we want all progress reports to follow the currently selected mode (coalesce or individual) or if we want them to be able to pick which one they are. I would vote to have a global setting that controls if we deliver things, but would love to hear what others think. Each Progress instance can have a debugger, but most don't, and those will get delivered to all debugger instances. So I would say that this map should live in each debugger object because So my suggestion here is to:
So the code in
Then we write a new There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Having the debugger instance keep track of a map of progress reports falls in line with what I spoke about with Ismail offline and is the direction I'd vote to go in so that the bookkeeping isn't done in the progress objects (I'm sure this isn't impossible, but given that LLDB is hierarchical it would make more sense that debuggers have progress reports rather than progress reports looking after themselves). So we still want to keep There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Correct. Because we have to just in case the debugger pointer was set in the Progress, so we have to track it in each debugger just in case. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. What's the benefit of doing this at the debugger level? The way I imagined this to work is like the other subsystems (similar to what Greg described inline). The downside of doing the bookkeeping in the debugger, is that we'll have to duplicate all this work across debuggers for progress events that are not tied to a single debugger. I don't know how easy it would be to change that (if we have access to the debugger everywhere we report progress today). Either way I'd like to understand the motivation behind it. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. llvm::StringMap? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. |
||||||
/// The title of the progress activity. | ||||||
std::string m_title; | ||||||
std::string m_details; | ||||||
|
@@ -117,6 +127,7 @@ class Progress { | |||||
/// to ensure that we don't send progress updates after progress has | ||||||
/// completed. | ||||||
bool m_complete = false; | ||||||
bool m_type; | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is confusing, why would you store your enum value in a There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Holdover from when I used a bool for this value before switching an enum when I put the patch up. |
||||||
}; | ||||||
|
||||||
} // namespace lldb_private | ||||||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -9,26 +9,34 @@ | |
#include "lldb/Core/Progress.h" | ||
|
||
#include "lldb/Core/Debugger.h" | ||
#include "lldb/Utility/StreamString.h" | ||
|
||
#include <optional> | ||
|
||
using namespace lldb; | ||
using namespace lldb_private; | ||
|
||
std::atomic<uint64_t> Progress::g_id(0); | ||
std::atomic<uint64_t> Progress::g_refcount(1); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This isn't needed. This value doesn't change and is always 1, so you can either make it a constexpr in the class description, or it isn't really needed at all actually. |
||
std::unordered_map<std::string, uint64_t> Progress::g_map = {}; | ||
Comment on lines
18
to
+20
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. When something isn't just an integer, like the map here, we can run into issues with this in a multi-threaded environment. Why? These variables are part of the global C++ constructor / destructor chain. If any thread uses these after the main thread shuts down it can cause a crash. So for global objects we typically will create a pointer to one and leak it with a note. See This map also need to be threadsafe, so it needs a mutex. Might be a good idea to create a class to contain these and use an accessor to provide access to it. The solution below will make something that is thread safe and also something that will survive the global destructor chain being called and letting other threads still be able to use Progress objects without crashing the process:
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is much better implementation of using a global map, but if we go with having the map being kept track of in each debugger object we might not need to have this implementation of a global map anymore. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. yes, might not be needed. Still needs to be threadsafe though if we do move it into the Debugger.h/.cpp There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. When you move it to the debugger instance, you don't need this anymore. |
||
|
||
Progress::Progress(std::string title, std::string details, | ||
std::optional<uint64_t> total, | ||
lldb_private::Debugger *debugger) | ||
lldb_private::Debugger *debugger, bool type) | ||
: m_title(title), m_details(details), m_id(++g_id), m_completed(0), | ||
m_total(Progress::kNonDeterministicTotal) { | ||
m_total(Progress::kNonDeterministicTotal), m_type(type) { | ||
if (total) | ||
m_total = *total; | ||
|
||
if (debugger) | ||
m_debugger_id = debugger->GetID(); | ||
std::lock_guard<std::mutex> guard(m_mutex); | ||
|
||
if (m_type == Progress::eProgressCoalecseReports) { | ||
g_map.emplace(title, g_refcount); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Using a global refcount shouldn't be necessary here, we can just initialize the value to 1 and increment/decrement as needed. |
||
|
||
if (g_map.at(title) >= 1) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This needs to be strictly greater than 1, otherwise you'll increment the refcount twice in the first progress report instantiation. |
||
++g_map.at(title); | ||
Comment on lines
+35
to
+38
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Use the threadsafe
|
||
} | ||
ReportProgress(); | ||
} | ||
|
||
|
@@ -38,6 +46,13 @@ Progress::~Progress() { | |
std::lock_guard<std::mutex> guard(m_mutex); | ||
if (!m_completed) | ||
m_completed = m_total; | ||
|
||
if (m_type == Progress::eProgressCoalecseReports) { | ||
--g_map.at(m_title); | ||
if (g_map.at(m_title) == 0) | ||
g_map.erase(m_title); | ||
Comment on lines
+51
to
+53
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Use the threadsafe
|
||
} | ||
|
||
ReportProgress(); | ||
} | ||
|
||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Unless you need the sorted iteration property you probably want to use something more efficient instead.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I shouldn't need a sorted map so I'll go with StringMap for the updated patch with the class that will handle the progress report map. 👍🏾