Multiple platforms with the same name

Hello all,

currently our code treats platform name more-or-less as a unique identifier (e.g. Platform::Find returns at most one platform instance --the first one it finds).

This is why I was surprised that the "platform select" CLI command always creates a new instance of the given platform, even if the platform of a given name already exists. This is because Platform::Create does not search the existing platform list before creating a new one. This might sound reasonable at first, but for example the Platform::Create overload which takes an ArchSpec first tries to look for a compatible platforms among the existing ones before creating a new one.

For this reason, I am tempted to call this a bug and fix the name-taking Create overload. This change passes the test suite, except for a single test, which now gets confused because some information gets leaked from one test to another. (although our coverage of the Platform class in the tests is fairly weak)

However, this test got me thinking. It happens to use the the SB way of manipulating platforms, and "creates" a new instance as lldb.SBPlatform("remote-linux"). For this kind of a command, it would be reasonable/expected to create a new instance, were it not for the fact that this platform would be very tricky to access from the command line, and even through some APIs -- SBDebugger::CreateTarget takes a platform _name_.

So, which one is it? Should we always have at most one instance of each platform, or are multiple instances ok?

cheers,
pl

PS: In case you're wondering about how I run into this, I was trying to create a pre-configured platform instance in (e.g.) an lldbinit file, without making it the default. That way it would get automatically selected when the user opens an executable of the appropriate type. This actually works, *except* for the case when the user selects the platform manually. That's because in that case, we would create an empty/unpopulated platform, and it would be the one being selected because it was the /current/ platform.

Platforms can contain connection specific setting and data. You might want to create two different "remote-linux" platforms and connect each one to a different remote linux machine. Each target which uses this platform would each be able to fetch files, resolve symbol files, get OS version/build string/kernel info, get set working directory from the remote server they are attached. Since each platform tends to belong to a target and since you might want to create two different targets and have each one connected to a different remote machine, I believe it is fine to have multiple instances.

I would vote to almost always create a new instance unless it is the host platform. Though it should be possible to create to targets and possibly set the platform on one target using the platform from another that might already be connected.

I am open to suggestions if anyone has any objections.

Greg

I agree that permitting multiple platforms would be a more principled position, but it was not clear to me if that was ever planned to be the case.

If it was (or if we want it to be), then I think we need to start making bigger distinctions between the platform plugins (classes), and the actual instantiations of those classes. Currently there is no way to refer to "older" instances of the platforms as they all share the same name (the name of the plugin). Like, you can enumerate them through SBDebugger.GetPlatformAtIndex(), but that's about the only thing you can do as all the interfaces (including the SB ones) take a platform _name_ as an argument. This gets particularly confusing as in some circumstances we end up choosing the newer one (e.g. if its the "current" platform) and sometimes the older.

If we want to do that, then this is what I'd propose:
a) Each platform plugin and each platform instance gets a name. We enforce the uniqueness of these names (within their category).
b) "platform list" outputs two block -- the list of available plugins and the list of plugin instances
c) a new "platform create" command to create a platform
   - e.g. "platform create my-arm-test-machine --plugin remote-linux"
d) "platform select" selects the platform with the given /instance/ name
   - for convenience and compatibility if the name does not refer to any existing platform instance, but it *does* refer to a platform plugin, it would create a platform instance with the same name as the class. (So the first "platform select remote-linux" would create a new instance (also called remote-linux) and all subsequent selects would switch to that one -- a change to existing behavior)
e) SBPlatform gets a static factory function taking two string arguments
f) existing SBPlatform constructor (taking one string) creates a new platform instance with a name selected by us (remote-linux, remote-linux-2, etc.), but its use is discouraged/deprecated.
g) all other existing APIs (command line and SB) remain unchanged but any "platform name" argument is taken to mean the platform instance name, and it has the "platform select" semantics (select if it exists, create if it doesn't)

I think this would strike a good balance between a consistent interface and preserving existing semantics. The open questions are:
- is it worth it? While nice in theory, personally I have never actually needed to connect to more than one machine at the same time.
- what to do about platform-specific settings. The functionality has existed for a long time, but there was only one plugin (PlatformDarwinKernel) using it. I've now added a bunch of settings to the qemu-user platform on the assumption that there will only be one instance of the class. These are global, but they would really make more sense on a per-instance basis. We could either leave it be (I don't need multiple instances now), or come up with a way to have per-platform settings, similar like we do for targets. We could also do something with the "platform settings" command, which currently only sets the working directory.

Let me know what you think,
Pavel

Platforms can contain connection specific setting and data. You might want to create two different "remote-linux" platforms and connect each one to a different remote linux machine. Each target which uses this platform would each be able to fetch files, resolve symbol files, get OS version/build string/kernel info, get set working directory from the remote server they are attached. Since each platform tends to belong to a target and since you might want to create two different targets and have each one connected to a different remote machine, I believe it is fine to have multiple instances.
I would vote to almost always create a new instance unless it is the host platform. Though it should be possible to create to targets and possibly set the platform on one target using the platform from another that might already be connected.
I am open to suggestions if anyone has any objections.
Greg

I agree that permitting multiple platforms would be a more principled position, but it was not clear to me if that was ever planned to be the case.

We made a choice early on in lldb that it would be a one-to-many debugger (as opposed to gdb where you use one gdb process to debug one inferior). The idea was to allow people who have more complex inter-app communications to use the scripting features of lldb to make the process of debugging IPC and such-like more natural (like a “step-in” that steps across process boundaries when you step into a message dispatch). Or to run two instances that are slightly different and compare the paths through some bit of code. Or other cool uses we hadn’t thought of. I don’t do this kind of debugging much either, but then I just debug lldb all the time which is a fairly simple process, and it’s communication with the stub is pretty simple. So I don’t think that’s dispositive for how useful this design actually is...

Since the Platform class holds details about the current debug sessions on that platform, it has to take part in this design, which means either allowing one Platform to connect to all instances of it’s kind that lldb might want to debug, or making one Platform per instance. The latter design was what we had always intended, it is certainly how we’ve talked about it for as long as I can remember. OTOH, the whole Platform class is a bit of a mashup, since it holds both “things you need to know about a class of systems in order to debug on them” and “the connection you make to a particular instance”. I think the intention would be clearer if we separated the “PlatformExpert” part of Platform and the “the Remote machine I’m talking to” part of Platform.

If it was (or if we want it to be), then I think we need to start making bigger distinctions between the platform plugins (classes), and the actual instantiations of those classes. Currently there is no way to refer to "older" instances of the platforms as they all share the same name (the name of the plugin). Like, you can enumerate them through SBDebugger.GetPlatformAtIndex(), but that's about the only thing you can do as all the interfaces (including the SB ones) take a platform _name_ as an argument. This gets particularly confusing as in some circumstances we end up choosing the newer one (e.g. if its the "current" platform) and sometimes the older.

If we want to do that, then this is what I'd propose:
a) Each platform plugin and each platform instance gets a name. We enforce the uniqueness of these names (within their category).
b) "platform list" outputs two block -- the list of available plugins and the list of plugin instances
c) a new "platform create" command to create a platform
- e.g. "platform create my-arm-test-machine --plugin remote-linux"
d) "platform select" selects the platform with the given /instance/ name
- for convenience and compatibility if the name does not refer to any existing platform instance, but it *does* refer to a platform plugin, it would create a platform instance with the same name as the class. (So the first "platform select remote-linux" would create a new instance (also called remote-linux) and all subsequent selects would switch to that one -- a change to existing behavior)
e) SBPlatform gets a static factory function taking two string arguments
f) existing SBPlatform constructor (taking one string) creates a new platform instance with a name selected by us (remote-linux, remote-linux-2, etc.), but its use is discouraged/deprecated.
g) all other existing APIs (command line and SB) remain unchanged but any "platform name" argument is taken to mean the platform instance name, and it has the "platform select" semantics (select if it exists, create if it doesn't)

I think this would strike a good balance between a consistent interface and preserving existing semantics. The open questions are:
- is it worth it? While nice in theory, personally I have never actually needed to connect to more than one machine at the same time.

Definitely worth it IMO. I don’t know about you, but I mostly debug lldb which is a pretty simple debugging scenario. There are lots of complicated things folks do with lldb that I seldom do...

Also, it would be weird if we went to all this effort to make it possible for one lldb to debug several processes at the same time, even allowing it if the two processes are on different platforms, but disallowed it if the processes happened to be on two machines with the same platform.

- what to do about platform-specific settings. The functionality has existed for a long time, but there was only one plugin (PlatformDarwinKernel) using it. I've now added a bunch of settings to the qemu-user platform on the assumption that there will only be one instance of the class. These are global, but they would really make more sense on a per-instance basis. We could either leave it be (I don't need multiple instances now), or come up with a way to have per-platform settings, similar like we do for targets. We could also do something with the "platform settings" command, which currently only sets the working directory.

W.r.t. the qemu settings, at least the architecture and env-vars are specific to the instance, so it really seems like these should be per instance; setting the working directory is also something that is specific to one targeted system. I could also imagine if you were working on qemu, being able to step through something running on two instances at the same time might be useful for figuring out why one or the other qemu instance was misbehaving. So “how to launch” the emulator also seems like it should be per-instance.

Moreover, it seems to me that if you wanted to debug a problem with two machine intercommunication where you were following message flow from a process on machine A to one on machine B, having two VM’s talking to one another would be a very convenient workbench for those investigations. So supporting multiple instances seems worth doing.

Jim

Platforms can contain connection specific setting and data. You might want to create two different "remote-linux" platforms and connect each one to a different remote linux machine. Each target which uses this platform would each be able to fetch files, resolve symbol files, get OS version/build string/kernel info, get set working directory from the remote server they are attached. Since each platform tends to belong to a target and since you might want to create two different targets and have each one connected to a different remote machine, I believe it is fine to have multiple instances.
I would vote to almost always create a new instance unless it is the host platform. Though it should be possible to create to targets and possibly set the platform on one target using the platform from another that might already be connected.
I am open to suggestions if anyone has any objections.
Greg

I agree that permitting multiple platforms would be a more principled position, but it was not clear to me if that was ever planned to be the case.

This code definitely evolved as time went on. Then we added the remote capabilities. As Jim said, there are two parts for the platform that _could_ be separated: PlatformLocal and PlatformRemote. Horrible names that can be improved upon, I am sure, but just names I quickly came up with.

PlatformLocal would be "what can I do for a platform that only involves finding things on this machine for supporting debugging on a remote platform". This would involve things like:
- where are remote files cached on the local machine for easy access
- where can I locate SDK/NDK stuff that might help me for this platform
- what architectures/triples are supported by this platform so it can be selected
- how to start a debug session for a given binary (which might use parts of PlatformRemote) as platforms like "iOS-simulator" do not require any remote connections to be able to start a process. Same could happen for VM based debugging on a local machine.

PlatformRemote
- get/put files
- get/set working directory
- install executable so OS can see/launch it
- create/delete directories

So as things evolved, everything got thrown into the Platform case and we just made things work as we went. I am sure this can be improved.

If it was (or if we want it to be), then I think we need to start making bigger distinctions between the platform plugins (classes), and the actual instantiations of those classes. Currently there is no way to refer to "older" instances of the platforms as they all share the same name (the name of the plugin). Like, you can enumerate them through SBDebugger.GetPlatformAtIndex(), but that's about the only thing you can do as all the interfaces (including the SB ones) take a platform _name_ as an argument. This gets particularly confusing as in some circumstances we end up choosing the newer one (e.g. if its the "current" platform) and sometimes the older.

If we want to do that, then this is what I'd propose:
a) Each platform plugin and each platform instance gets a name. We enforce the uniqueness of these names (within their category).

Maybe it would be better to maintain the name, but implement an instance identifier for each platform instance?

b) "platform list" outputs two block -- the list of available plugins and the list of plugin instances

If we added a instance identifier, then we could just show the available plug-in names followed by their instances?

c) a new "platform create" command to create a platform
- e.g. "platform create my-arm-test-machine --plugin remote-linux"

Now we are assuming you want to connect to a remote machine when we create platform? "platform connect" can be used currently if we want to actually connect to a remote platform, but there is a lot of stuff in the iOS platforms that really only deals with finding stuff on the local machine. Each platform plugin in "platform connect" has the ability to create its own unique connection arguments and options which is nice for different platforms.

The creation and connecting should still be done separately. Seeing the arguments you added above leads me to believe this is like a "select" and a "connect" all in one. And each "platform connect" has unique and different arguments and options that are tailored to each plug-in currently.

d) "platform select" selects the platform with the given /instance/ name
- for convenience and compatibility if the name does not refer to any existing platform instance, but it *does* refer to a platform plugin, it would create a platform instance with the same name as the class. (So the first "platform select remote-linux" would create a new instance (also called remote-linux) and all subsequent selects would switch to that one -- a change to existing behavior)

that is fine. We just have to make sure that this still works:

(lldb) platform select remote-linux
(lldb) file /path/to/ios/binary/Foo.app

The target created with "/path/to/ios/binary/Foo.app" should select the "remote-ios" platform. It is fine to always check if "remote-linux" platform would work for this binary first, but when it clearly doesn't, it should fall back to the mechanism that we currently have where we auto select a compatible platform.

The other option is to let "platform select <name>" create a new instance of the platform if an instance already exists? If we are in interactive mode, like in the command interpreter, we can query the user to say "There is already an instance of platform "<name>" that exists, do you want to create a new one?".

Might also be nice to add a new option that allows us to create a new instance with "--always-create" or something like that in case we do want to have two instances with the same plug-in. Then users of course can select the platform with the unique instance name.

e) SBPlatform gets a static factory function taking two string arguments

What are the two arguments? We can't change existing LLDB API, but we can add more. Keep that in mind as we ponder changes.

f) existing SBPlatform constructor (taking one string) creates a new platform instance with a name selected by us (remote-linux, remote-linux-2, etc.), but its use is discouraged/deprecated.
g) all other existing APIs (command line and SB) remain unchanged but any "platform name" argument is taken to mean the platform instance name, and it has the "platform select" semantics (select if it exists, create if it doesn't)

I think this would strike a good balance between a consistent interface and preserving existing semantics. The open questions are:
- is it worth it? While nice in theory, personally I have never actually needed to connect to more than one machine at the same time.

I don't want to prevent it. So if we can design a way for this to be able to happen still, I would like to see us not disallow this somehow.

- what to do about platform-specific settings. The functionality has existed for a long time, but there was only one plugin (PlatformDarwinKernel) using it. I've now added a bunch of settings to the qemu-user platform on the assumption that there will only be one instance of the class. These are global, but they would really make more sense on a per-instance basis. We could either leave it be (I don't need multiple instances now), or come up with a way to have per-platform settings, similar like we do for targets. We could also do something with the "platform settings" command, which currently only sets the working directory.

Each setting has the option of saying "I am a global setting" or "I am an instance setting". It should be easy to set the right settings up as global or instance based right?

Let me know what you think,
Pavel

So a few things about platforms as they currently work in LLDB: we currently rely on many targets being able to share the same platform when running the remote test suites. This allows you to select the platform, connect to "lldb-server platform ..." that is running on the remote machine, and then create as many targets as you want and debug to that connected platform. We need to make sure this flow is still possible with any changes we make.

I'm sorry for the slow response. I had to attend to some other things first. It sounds like there's agreement to support multiple platform instances, so I'll try to move things in that direction.

Further responses inline

Platforms can contain connection specific setting and data. You might want to create two different "remote-linux" platforms and connect each one to a different remote linux machine. Each target which uses this platform would each be able to fetch files, resolve symbol files, get OS version/build string/kernel info, get set working directory from the remote server they are attached. Since each platform tends to belong to a target and since you might want to create two different targets and have each one connected to a different remote machine, I believe it is fine to have multiple instances.
I would vote to almost always create a new instance unless it is the host platform. Though it should be possible to create to targets and possibly set the platform on one target using the platform from another that might already be connected.
I am open to suggestions if anyone has any objections.
Greg

I agree that permitting multiple platforms would be a more principled position, but it was not clear to me if that was ever planned to be the case.

This code definitely evolved as time went on. Then we added the remote capabilities. As Jim said, there are two parts for the platform that _could_ be separated: PlatformLocal and PlatformRemote. Horrible names that can be improved upon, I am sure, but just names I quickly came up with.

PlatformLocal would be "what can I do for a platform that only involves finding things on this machine for supporting debugging on a remote platform". This would involve things like:
- where are remote files cached on the local machine for easy access
- where can I locate SDK/NDK stuff that might help me for this platform
- what architectures/triples are supported by this platform so it can be selected
- how to start a debug session for a given binary (which might use parts of PlatformRemote) as platforms like "iOS-simulator" do not require any remote connections to be able to start a process. Same could happen for VM based debugging on a local machine.

PlatformRemote
- get/put files
- get/set working directory
- install executable so OS can see/launch it
- create/delete directories

So as things evolved, everything got thrown into the Platform case and we just made things work as we went. I am sure this can be improved.

I actually have a branch where I've tried to separate the local and remote cases, and remove the if(IsHost()) checks everywhere, but I haven't found yet found the time to clean it up and send an rfc.

If it was (or if we want it to be), then I think we need to start making bigger distinctions between the platform plugins (classes), and the actual instantiations of those classes. Currently there is no way to refer to "older" instances of the platforms as they all share the same name (the name of the plugin). Like, you can enumerate them through SBDebugger.GetPlatformAtIndex(), but that's about the only thing you can do as all the interfaces (including the SB ones) take a platform _name_ as an argument. This gets particularly confusing as in some circumstances we end up choosing the newer one (e.g. if its the "current" platform) and sometimes the older.

If we want to do that, then this is what I'd propose:
a) Each platform plugin and each platform instance gets a name. We enforce the uniqueness of these names (within their category).

Maybe it would be better to maintain the name, but implement an instance identifier for each platform instance?

I'm not sure what you mean by that. Or, if you mean what I think you mean, then we're actually in agreement. Each platform plugin (class) gets a name (or identifier, or whatever we want to call it), and each instance of that class gets a name as well.

Practically speaking, I think we could reuse the existing GetPluginName and GetName (currently hardwired to return GetPluginName()). The former would return the plugin name, and the latter would give the "instance identifier".

b) "platform list" outputs two block -- the list of available plugins and the list of plugin instances

If we added a instance identifier, then we could just show the available plug-in names followed by their instances?

Yes, that would just be a different (and probably better) way of displaying the same information. We can definitely do that.

c) a new "platform create" command to create a platform
  - e.g. "platform create my-arm-test-machine --plugin remote-linux"

Now we are assuming you want to connect to a remote machine when we create platform? "platform connect" can be used currently if we want to actually connect to a remote platform, but there is a lot of stuff in the iOS platforms that really only deals with finding stuff on the local machine. Each platform plugin in "platform connect" has the ability to create its own unique connection arguments and options which is nice for different platforms.

The creation and connecting should still be done separately. Seeing the arguments you added above leads me to believe this is like a "select" and a "connect" all in one. And each "platform connect" has unique and different arguments and options that are tailored to each plug-in currently.

I'm not assuming anything here. "my-arm-test-machine" is just an instance identifier for this particular platform. It does not have to correspond to any existing domain name or anything else.

The command should be read as "create a platform instance with the name 'my-arm-test-machine' using the plugin 'remote-linux'. It would create the instance in the default (for remote platforms, that means unconnected) state. To connect, one would still need to issue a platform connect command.

d) "platform select" selects the platform with the given /instance/ name
  - for convenience and compatibility if the name does not refer to any existing platform instance, but it *does* refer to a platform plugin, it would create a platform instance with the same name as the class. (So the first "platform select remote-linux" would create a new instance (also called remote-linux) and all subsequent selects would switch to that one -- a change to existing behavior)

that is fine. We just have to make sure that this still works:

(lldb) platform select remote-linux
(lldb) file /path/to/ios/binary/Foo.app

The target created with "/path/to/ios/binary/Foo.app" should select the "remote-ios" platform. It is fine to always check if "remote-linux" platform would work for this binary first, but when it clearly doesn't, it should fall back to the mechanism that we currently have where we auto select a compatible platform.

Yes, I have no intention of changing that.

The other option is to let "platform select <name>" create a new instance of the platform if an instance already exists? If we are in interactive mode, like in the command interpreter, we can query the user to say "There is already an instance of platform "<name>" that exists, do you want to create a new one?".

Might also be nice to add a new option that allows us to create a new instance with "--always-create" or something like that in case we do want to have two instances with the same plug-in. Then users of course can select the platform with the unique instance name.

Personally, I have found it very confusing that something called "select" actually creates an instance. Having an explicit "create" command would be more consistent with "target create" for instance.

That said, the syntax I proposed above is somewhat verbose, and I am definitely open to changing that. One possibility would be to turn the names around and have "platform create remote-linux --name foo", where if you don't specify the name argument, then the lldb automatically generates a new name.

Theoretically we could even drop instance names completely, and refer to platforms by their index (like we do for targets, etc.), but then we'd have to do something about the APIs which already expect a name.

e) SBPlatform gets a static factory function taking two string arguments

What are the two arguments? We can't change existing LLDB API, but we can add more. Keep that in mind as we ponder changes.

The plugin name and the instance name. So,

my_new_platform = lldb.SBPlatform.Create("remote-linux", "foo")

would create an instance of the remote-linux plugin, and call it "foo".

Each setting has the option of saying "I am a global setting" or "I am an instance setting". It should be easy to set the right settings up as global or instance based right?

That might be possible, though I'm not sure what it would take to do that. I don't think we have any instance-specific plugin settings at the moment.

It would be slightly weird though, because the plugin settings include plugin names in their paths. Unlike say target.run-args, where that name always refers to the currently selected target, plugin specific settings have names like platform.plugin.qemu-user.emulator-path. That raises questions like what should that name refer to if the currently selected platform is not of the "qemu-user" class.

I think the only reasonable answer is "the global version of that setting", but that still feels a bit strange. In a way, it might be better if we had a "platform" setting, which always referred to settings for the current platform, but I am not totally happy with that idea either (then we'd have settings appearing and disappearing based on the current selection).

But I don't think we need to solve this right now. I think we can first properly support multiple platform instances, and then figure out what to do about settings.

pl

This week I tried to make some progress on this front. The first thing I wanted to do is to print a list of actual platform instances (⚙ D119131 [lldb] List platform plugin *instances* in "platform list"), but while doing that, I ran into another interesting problem.

Currently, we have two lists of platform instances. We have a global list of all platform instances (GetPlatformList in Platform.cpp), and also a per-debugger list in Debugger::m_platform_list.

Does anyone know why we have two lists and what is their intended relationship?

Having two lists seems confusing to me. I think it would be better to either have one global list, shared across all debuggers (the concept of a “current” platform could stay debugger-specific). Or, have a per-debugger list of platform, and make sure that a debugger only uses the platform it is associated with.

My patch currently prints the debugger-specific list, but I don’t think there’s anything preventing a debugger from creating a target with a platform from another debugger instance, so I fear the output may be confusing. If we really want to have two lists, then I guess I ought to also print the global list too…

Without knowing the answer to your previous question and purely based on the previous discussion, it sounded to me like we should have a list per debugger. At least that’s how I understood Jim’s description of being able to have different platforms for different targets.

So I think we should have:

  • A per debugger* list of available platforms, not instances.
  • A per debugger list of platform instances.
  • A per debugger selected platform (the type of platform that will be instantiated unless specified explicitly)

(*) This could be global if building this list were expensive, but I assume it would be much easier from an implementation point of view to have all of this live in the per-debugger platform list.

This is basically a list of registered platform plugins. It is currently maintained (like any other plugin) by the PluginManager. I don’t currently see a reason to change that.

It think that would be the best solution, but it’s going to be trickier to implement, as we’ll need to come up with a migration plan for the SB API. Currently SBPlatform constructor creates a floating platform not attached to any debugger, but still registered in the global platform list. Setting it as a current platform of a debugger also adds it to the list of platforms for that debugger as well. This happens for each debugger it encounters.

I suppose we could keep that floating platform concept (in a deprecated/discouraged fashion) even without a global platform list. The platform could still be lazily bound to a debugger(s), but until that happens, there would be essentially be no way to use it. If we wanted to ensure that a single platform is used by (at most) one debugger, we could have subsequent debuggers refuse to add it to their list in case it is “bound” already. The “new” way of creating platforms would be via something like SBDebugger::CreatePlatform, which would create an already-bound instance (like we do for targets).

We would also need to make some modifications to the test suite, as it currently a new debugger instance for each test method, but (in the remote mode) a platform is created once for the entire test file. Now we would have a per-test-case platform as well.

Does somebody need to own the platform per se? Can we say that when you create a platform with the SBPlatform constructor, it’s not bound to anything, until you either call SBDebugger::SetSelectedPlatform or, if we think we need this, a new API to bind it to a debugger without making it the select platform.

With the approach I describe above we could make a new SBPlatform once and then make it the selected platform for every debugger we instantiate. I realize this goes agains the whole idea of making keeping the platform per debugger, but given we’d have to support this for legacy reasons anyway, it might ease the transition.

So one way to fix the issue of a global list and a debugger specific list is to have the platform instance have a weak pointer to the debugger it is associated with. If the platform gets created via the API, that starts out as an invalid weak pointer, but once the platform gets used by a target, it would update the platform object’s weak pointer to the debugger. Platform instances could also track the targets that are associated with the object with a vector of weak pointers to the targets they are associated with.

If someone creates a platform with “platform select”, the debugger would be auto filled in with a weak pionter to the debugger that owns the command interpreter.

If someone creates a Platform through the SB API with just a name argument, then it starts off with no debugger weak pointer, but then it would get set when it is associated with a target.

We could maintain only a global list and have if the platform objects have a weak pointer to the debuggers they are associated with. Then any command that wants platforms of a given debugger can compare to make sure the debugger matches. This would allow us to still allow a platform be created via the SB API, and then later associated with a debugger when a target is associated with it.

Yes, that would definitely be possible, and easier to implement. And it would still avoid the “spooky action at a distance” because only the platforms that have been explicitly registered with a debugger would end up being. It would still allow a single platform to be registered with multiple debuggers, which could be either a bug or a feature, depending on how you look at it.

That is possible too, but before we get to implementation details like weak pointers, I’d like us to agree on what it is that we’re actually implementing – what’s the kind of behavior that we want. So answering question’s like:

  1. Do we want to have platforms associated with multiple debuggers?
    In my original proposal, the answer was “no”. In Jonas’s, it’s “yes”. Yours, I believe, is also “no”.

  2. If no, how to we get to there from the current state?
    My answer was “have the debugger-less platforms bind themselves to the first debugger they meet”. Jonas’s doesn’t require any transition. I think yours was “do the binding via weak pointers”.

  3. When does a platform get associated with a debugger?
    (Some of ) the possible answers are:

  • only when its set as the current platform (or some other explicit action) – I think this is what me and Jonas were proposing
  • also when creating a target, but only if the platform is explicitly mentioned by name
  • when creating a target, if the platforms claims it supports the targets architecture (note that this is only a question for unassociated platforms – associated platforms would always be able to do this) - this is kind of the status quo, except that there is an under-defined two tier system, where the associated platforms are picked first.

If we can answer these, then we can figure out what kinds of lists to maintain. E.g., as long as there is any chance of a platform being used with more than one debugger, then we will most likely need to maintain some kind of a global list to ensure the uniqueness of their identifiers. However, we wouldn’t need to consult this list for anything else.

I didn’t think about the uniqueness of the identifiers. I was really hoping we could get rid of the global list, but it sounds that there’s no good way to do that without a solution for the floating SBAPI case. In that case I’m happy to get behind the original proposal and say a platform cannot be associated with more than one debugger.

After thinking about this further, I’ve realized that the connection between shared platforms, global lists and identifiers is not as simple as I originally thought.

One can imagine an implementation with shared platforms and no global lists. For example we could enforce identifier uniqueness at association time, and refuse to associate a platform with a debugger if the debugger already happens to have a platform with the same name. However, that solution would not be my first choice.

On the other hand, if we want to avoid global lists and still handle floating platforms, then we’d need to delay the creation of a platform identifier until it is associated with a debugger (so we can generate a unique one). Also not ideal, but I think it should be fine, as the identifier is mainly useful for the command interpreter – in the SB API we can use the SBPlatform object directly.

So, if we go with the platform-bound-to-single-debugger idea and no global lists, then this is what I’d propose:

  • The new (non-deprecated) way to create platforms becomes SBDebugger::CreatePlatform(), which instantly binds the platform to a debugger. The identifier is either assigned by the user or automatically generated.
  • Platforms cannot be associated with more than one debugger. Attempts to associate with the second one fail. The answer to my first question is “no”.
  • Floating (debugger-less, created through legacy apis) platforms bind to the first debugger they meet, and get the identifier automatically assigned then. (second question)
  • Third question: The association happens at creation time (new api), or when the platform is first time set as the “current” platform (for floating legacy platforms).
  • When creating a target, only platforms associated with the debugger are eligible for selection (using the existing logic). If no platform claims the target, we create a new platform instance, instead of trying platforms from the global list (which won’t exist).
  • The association will /probably/ be implemented via weak pointers, but I’ll know more when I actually try to implement this.

How does that sound?

Sounds good to me. I’m curious about the memory ownership of that plan, but that’s something we can discuss in the patch.