This is an archive of the discontinued LLVM Phabricator instance.

Introduce chrono to more gdb-remote functions
ClosedPublic

Authored by labath on Nov 22 2016, 6:23 AM.

Details

Summary

This replaces the usage of raw integers with duration classes in the gdb-remote
packet management functions. The values are still converted back to integers once
they go into the generic Communication class -- that I am leaving to a separate
change.

The changes are mostly straight-forward (*). The thing I'd most like feedback on
is the representation of timeouts.

Currently, we use UINT32_MAX to denote infinite timeout. This is not well suited
for duration classes, as they tend to do arithmetic on the values, and the
identity of the MAX value can easily get lost (e.g.
microseconds(seconds(UINT32_MAX)).count() != UINT32_MAX). We cannot use zero to
represent infinity (as Listener classes do) because we already use it to do
non-blocking polling reads. For this reason, I believe it is better to have an
explicit value for infinity.

The way I achieved that is via llvm::Optional, and I think it reads quite
natural. Passing llvm::None as "timeout" means "no timeout", while passing zero
means "poll". The only tricky part is this breaks implicit conversions (seconds
are implicitly convertible to microseconds, but Optional<seconds> cannot be
easily converted into Optional<microseconds>). To make this less annoying I have
switched a couple of places from using seconds to microseconds to make the code
flow better.

I think other places could also benefit from the same pattern (e.g., if Listener
functions accepted an llvm::Optional timeout, then we wouldn't need to have
separate Peek* and Get* versions of the functions).

(*) The other tricky part was GDBRemoteCommunication::PopPacketFromQueue, which
was needlessly complicated. I've simplified it, but that one is only used in
non-stop mode, and so is untested.

Diff Detail

Repository
rL LLVM

Event Timeline

labath updated this revision to Diff 78865.Nov 22 2016, 6:23 AM
labath retitled this revision from to Introduce chrono to more gdb-remote functions.
labath updated this object.
labath added reviewers: clayborg, zturner, jingham.
labath added a subscriber: lldb-commits.
clayborg requested changes to this revision.Nov 22 2016, 9:14 AM
clayborg edited edge metadata.

It would be nice if we can find a way to be able to use any time units we want when specifying the timeout. I would rather not have everyone have to convert their times to microseconds. Maybe we just add some overloads to the public facing functions and have just one internal function that uses llvm::Optional.

As a side note I would also like us to fix llvm::Optional so it can be debugged. Right now if you expand an llvm::Optional<T> you see the an array of characters instead of your type T which means when debugging you can't see your type's value. Boost correctly uses C++11 unions so that we can see the type when expanded in the debugger.

source/Plugins/Process/gdb-remote/GDBRemoteCommunication.cpp
136 ↗(On Diff #78865)

Can me make this clearer with something like:

#ifdef LLDB_CONFIGURATION_DEBUG
    m_packet_timeout(std::chrono::seconds(1000));
#else
    m_packet_timeout(std::chrono::seconds(1));
#endif
source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.cpp
125 ↗(On Diff #78865)

Can't we do:

const std::chrono::milliseconds timeout(10);

Or is this what you mentioned with the llvm::Optional stuff in your description?

206 ↗(On Diff #78865)

It would be nice if we can just use std::chrono::seconds. I would hope this line doesn't need to change?

This revision now requires changes to proceed.Nov 22 2016, 9:14 AM
zturner edited edge metadata.Nov 22 2016, 9:20 AM

It would be nice if we can find a way to be able to use any time units we want when specifying the timeout. I would rather not have everyone have to convert their times to microseconds. Maybe we just add some overloads to the public facing functions and have just one internal function that uses llvm::Optional.

As a side note I would also like us to fix llvm::Optional so it can be debugged. Right now if you expand an llvm::Optional<T> you see the an array of characters instead of your type T which means when debugging you can't see your type's value. Boost correctly uses C++11 unions so that we can see the type when expanded in the debugger.

Couldn't someone just add a data formatter for llvm::Optional<> to lldb?

zturner added inline comments.Nov 22 2016, 9:32 AM
source/Plugins/Process/gdb-remote/GDBRemoteClientBase.cpp
56 ↗(On Diff #78865)

I think we should all be willing to agree that using namespace std::chrono is fine in CPP files. The namespace is not particularly large, and it makes code much nicer to read.

249 ↗(On Diff #78865)

You can also write using namespace std::chrono::literals at the top of the file, and then you can write:

ReadPacket(extra_stop_reply_packet, 100ms, false);

source/Plugins/Process/gdb-remote/GDBRemoteCommunication.cpp
136 ↗(On Diff #78865)

Or if using literals, 1000s or 1s

source/Plugins/Process/gdb-remote/GDBRemoteCommunication.h
83 ↗(On Diff #78865)

Could this class be templated on duration type? So we could use us, ms, or whatever pleased us?

232 ↗(On Diff #78865)

One idea might be to have

template <typename T>
using GdbTimeout = llvm::Optional<std::chrono::duration<int, T>

then you could write:

PacketResult ReadPacket(StringExtractorGDBRemote &response, GdbTimeout<std::micro> timeout);

I suppose we could create a new class (I am not sure what a good name for that would be... OptionalDuration, which inherits from llvm:Optional, but provides the additional conversion operator to make things work seamlessly. Something like:

template<typename Ratio>
class OptionalDur<Ratio>: public llvm::Optional<std::chrono::duration<Ratio>> {
  template<typename Ratio2, typename = std::enable_if<std::is_convertible<duration<Ratio2>, duration<Ratio>>>::type>
  OptionalDur(OptionalDur<Ratio2> other) { ...}
};

It will be a bunch of template-fu, but it can be made to work. What do you think? (I am also open to other ideas on how to implement this....)

For the formatting, I am not sure what it would take to make Optional use a union, but I can certainly make a data formatter for it. I've been planning to dig into that area soon anyway...

source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.cpp
125 ↗(On Diff #78865)

Yes, that's exactly what I meant.

labath added inline comments.Nov 22 2016, 9:54 AM
source/Plugins/Process/gdb-remote/GDBRemoteClientBase.cpp
56 ↗(On Diff #78865)

I am fine with that. Should we make that an official policy somewhere?

249 ↗(On Diff #78865)

I like literals as well, but I wasn't sure what is the general opinion about them.

source/Plugins/Process/gdb-remote/GDBRemoteCommunication.h
83 ↗(On Diff #78865)

You don't need templates for this. Thanks to implicit conversions it can be used with any type with a coarser granularity than microseconds. It's only when Optional comes into the picture that things start to break down.

232 ↗(On Diff #78865)

That sounds fine, although I would put the class in some place more general, as I plan to use it outside the gdb plugin as well.

It would be nice if we can find a way to be able to use any time units we want when specifying the timeout. I would rather not have everyone have to convert their times to microseconds. Maybe we just add some overloads to the public facing functions and have just one internal function that uses llvm::Optional.

I am not sure what would the public-facing functions be in this case. All of the code here is internal to the plugin, but I was definitely annoyed when I had to convert everything to microsecs. I am afraid we will have to make overloads of many functions to make the usages here clean.

I am going to try to see how hairy the new optional class ends up looking. I like that more as it concentrates the uglyness into one place.

labath updated this revision to Diff 78895.Nov 22 2016, 10:45 AM
labath edited edge metadata.

A new version, which uses a helper class which enables all the implicit
conversions that one would normally expect from the duration classes.

Things left TBD:

  • name
  • where to put it
  • whether inheriting from Optional is fine (it is slightly dodgy, but it reduces boilerplate).
  • using namespace std::chrono, where it makes sense.
labath updated this revision to Diff 79050.Nov 23 2016, 3:22 AM
labath edited edge metadata.

Changes since last version:

  • Rename the class to Timeout<>, to reflect the fact that I'd like to use it as a general method for describing timeouts.
  • add "using namespace std::chrono" to files with significat chrono usage.

I did not use chrono literals, as those are a c++14 feature. I don't think that
matters much as thanks to the previous import, something like seconds(X) is quite
short already.

Let me know what you think.

clayborg accepted this revision.Nov 23 2016, 9:12 AM
clayborg edited edge metadata.

Much better, looks good.

This revision is now accepted and ready to land.Nov 23 2016, 9:12 AM
This revision was automatically updated to reflect the committed changes.