This is an archive of the discontinued LLVM Phabricator instance.

[lldb] Make logging machinery type-safe
ClosedPublic

Authored by labath on Jan 17 2022, 7:00 AM.

Details

Summary

This patch makes use of c++ type checking and scoped enums to make
logging statements shorter and harder to misuse.

Defines like LIBLLDB_LOG_PROCESS are replaces with LLDBLog::Process.
Because it now carries type information we do not need to worry about
matching a specific enum value with the right getter function -- the
compiler will now do that for us.

The main entry point for the logging machinery becomes the GetLog
(template) function, which will obtain the correct Log object based on
the enum type. It achieves this through another template function
(LogChannelFor<T>), which must be specialized for each type, and should
return the appropriate channel object.

This patch also removes the ability to log a message if multiple
categories are enabled simultaneously as it was unused and confusing.

This patch does not actually remove any of the existing interfaces. The
defines and log retrieval functions are left around as wrappers around
the new interfaces. They will be removed in follow-up patch.

Diff Detail

Event Timeline

labath requested review of this revision.Jan 17 2022, 7:00 AM
labath created this revision.
Herald added a project: Restricted Project. · View Herald TranscriptJan 17 2022, 7:00 AM

I like the idea of the templates that would not allow bitfields from one log channel to be used on another log channel as there we a few incorrect ones in the GDB remote process plug-in. See inlined comments

lldb/include/lldb/Utility/Logging.h
15–16

Seems like using LLDBLog::Process everywhere instead of LIBLLDB_LOG_PROCESS would be cleaner? Are we just trying to reduce code changes?

19

Why wouldn't we just add all of the categories in the enum? Are we trying to reduce code changes?

20

Can we add members here to grab the log channels directly from the LLLDBLog enum? Something like:

static Log *GetLogIfAll(LLDBLog mask);
static Log *GetLogIfAny(LLDBLog mask);
lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemoteLog.cpp
17–18

Should the Log::Category be templatized with GDBRLog and then we just store GDBRLog enum values instead of uint32_t values?

18–22

If we do templatize Log::Category with GDBRLog (like "Log::Category<GDBRLog>") then we can just use the real enums here?

lldb/source/Plugins/Process/gdb-remote/ThreadGDBRemote.cpp
57

I would love these to be cleaner. Maybe something like:

Log *log = GDBRLog.GetLogIfAny(GDBRLog::Thread);
labath added inline comments.Jan 19 2022, 6:14 AM
lldb/include/lldb/Utility/Logging.h
15–16

Yes, the goal would be do delete these completely, but I'd split this into two patch -- first do the functional changes, and then do a search-replace to get rid of all the macros.

19

Yes, the full patch would definitely do that. This was just something I quickly whipped up to demonstrate how I thought this could work.

20

The idea is that there will be a single (templated) GetLogIfAny/All function (see Log.h:218), and that one will automatically select (based on the type of the enum) the right channel.

88–90

These functions are also just temporary

94

This is what ties an enum to a particular channel.

lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemoteLog.cpp
17–18

The problem with that is that it would require templatizing pretty much all of the logging code (as it deals with categories in one way or another), but I agree that we should at least make the category definitions nice. It should be possible to keep templates here, but then convert to integers when we get to the core logging code, but i did not want to get entagled in that for the prototype.

lldb/source/Plugins/Process/gdb-remote/ThreadGDBRemote.cpp
57

Log *log = GetLogIfAny(GDBRLog::Thread); is the final goal.

labath updated this revision to Diff 401909.Jan 21 2022, 2:35 AM

Do this for real

labath retitled this revision from [lldb] Log prototype to [lldb] Make logging machinery type-safe.Jan 21 2022, 2:36 AM
labath edited the summary of this revision. (Show Details)
labath added reviewers: clayborg, jingham, JDevlieghere.
labath marked 4 inline comments as done.Jan 21 2022, 2:49 AM

This should be ready for a full review now.

lldb/include/lldb/Utility/Logging.h
15

Bit zero was unused, so we still have one bit left, but I've created a new typedef for holding the category flags, so extending it should be easy.

lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemoteLog.cpp
17–18

I think I've mostly solved this by templatizing the Log::Category constructor (instead of the whole class). That way the generic code remains template free, and there are no casts in the user code. The constructor converts the enum values to integers, after verifying that they are of the right type.

lldb/source/Utility/Logging.cpp
28–40

This would also be a good time to bikeshed on the names, as some of these are fairly inconsistent (dyld vs DynamicLoader, event vs Events, jit vs JITLoader). I think we could standardise on the shorter, user-facing ones.

JDevlieghere added inline comments.Jan 21 2022, 9:25 AM
lldb/include/lldb/Utility/Log.h
57

Didn't this all start with Greg wanting to make this a uint64_t?

lldb/include/lldb/Utility/Logging.h
19–50

I would put this into a .def file and have it generate both this list as well as the one with the macros below. The last entry is annoying. We could either omit it from the list but then you risk someone updating the def file but not this. I don't think there's a way to do this automatically? Maybe just an additional define?

shafik added a subscriber: shafik.Jan 21 2022, 9:32 AM

This is a nice refactor, I am curious was there a motivating bug or issue for this change or just refactoring?

clayborg added inline comments.Jan 21 2022, 12:57 PM
lldb/include/lldb/Utility/Log.h
57

yes, uint64_t please!

lldb/include/lldb/Utility/Logging.h
56–86

I know we need these for now, but it would be great to get rid of these in follow up patches

lldb/source/Plugins/Process/POSIX/ProcessPOSIXLog.h
29–35

Do we need these #define lines?

lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemoteLog.h
32–46

Do we need these anymore?

lldb/unittests/Utility/LogTest.cpp
34–39

Should we make a macro for this in Log.h? Something that would define the log channel global variable, and make the template for us?

LLDB_CREATE_LOG(test_categories, TestChannel, TestChannel::FOO);

which would expand to the above code?

I do like the direction of this patch BTW!

labath marked 7 inline comments as done.Jan 24 2022, 1:03 PM

This is a nice refactor, I am curious was there a motivating bug or issue for this change or just refactoring?

Well.. my motivation was D117382. I don't know what motivated him to do the big refactor (I know he was running out of log category bits, but I think that could be solved without it), but one of the things both of us are trying to solve is to make sure one cannot mismatch log category flags and log getter function (we have some code doing that right now).

lldb/include/lldb/Utility/Log.h
57

I wanted to keep that for a separate patch. I was preparing for that by introducing the new type(def). Though, since this is now really just about changing this single identifier, I guess I might as well do it immediately.

lldb/include/lldb/Utility/Logging.h
19–50

I'm planning to delete the macros more-or-less immediately after this patch goes in. I didn't want to do that in the same patch as it would make hide the "interesting" changes between thousands of machanical edits.

So, I wouldn't want to create a def file just because of that. I can imagine creating both this list and the Log::Category definition with a def file, though I'm not sure if it's really worth it.

As for LLVM_MARK_AS_BITMASK_ENUM, if I was going with a def file, I'd probably make this LLVM_MARK_AS_BITMASK_ENUM(std::numeric_limits<MaskType>::max()) (it does not have to refer to an actual constant). The only thing we'd "lose" that way is some assertion failures if someone passes an out-of-range constant in some way...

lldb/unittests/Utility/LogTest.cpp
34–39

I thought about this for a long time. The macro adds some convenience, but it does not seem right to me. The macro would hide the fact that there are two objects being declared here, with different storage classes. The storage class part is important, because if one tried to put this declaration in a header, it could almost work, except that the LogChannelFor function would return a different (static) object in each compile unit (which would be an odr violation, of course).

And I am actually thinking about moving this stuff to a header, since ideally we'd want to compiler to inline the LogChannelFor function call. That would mean the user would need to forward-declare the Channel object in the header, and define it in the cpp file, and it would be weird to define an object declared by a macro.

I think that moving this to a header would also reduce the boilerplate slightly, since one wouldn't need to forward-declare *and* define the LogChannelFor specialization.

At the end of the day, I don't think this matters much, as we don't create logging channels very often.

labath updated this revision to Diff 402636.Jan 24 2022, 1:03 PM
labath marked an inline comment as done.
labath edited the summary of this revision. (Show Details)
  • use uint64_t for channel mask
  • ensure constants are in ascending order
JDevlieghere accepted this revision.Jan 24 2022, 1:19 PM

I'm happy with this. LGTM assuming Greg feels the same way.

This revision is now accepted and ready to land.Jan 24 2022, 1:19 PM
clayborg accepted this revision.Jan 24 2022, 4:18 PM

LGTM.

This revision was landed with ongoing or failed builds.Jan 25 2022, 3:14 AM
This revision was automatically updated to reflect the committed changes.
thakis added a subscriber: thakis.Jan 25 2022, 4:20 AM

Looks like this doesn't build on Mac: http://45.33.8.238/macm1/26315/step_4.txt

I'm sorry. I should have that fixed right very quickly.