News:

The new Release 25.03 is out! You can download binaries for Windows and many major Linux distros here .

Main Menu

Extend GDB plugin to communicate with other plugins

Started by BlueHazzard, September 25, 2016, 05:28:29 PM

Previous topic - Next topic

oBFusCATed

Quote from: BlueHazzard on July 31, 2017, 09:50:38 PM
With this kind of watch it would be possible to do all this things from a plugin.
I really doubt it. Especially if you want to transfer tons of data.

Quote from: BlueHazzard on July 31, 2017, 09:50:38 PM
At the moment i would like to implement it and for this i need to know what api the devs would support.
It will be good if you could try the minimal version of the watch idea. In order to see if it is worth pursuing. Nothing fancy and complex on the UI part. Get adding (use some hardcoded path to a svd file for example) and updating watches to work. My original thought was to put the register watches in the watches window, because it will make it easier to experiment with it.

Quote from: BlueHazzard on July 31, 2017, 09:50:38 PM
For optimization is later time, if the bottleneck is found.
The problem with api design is that you should design for performance up front. It is very hard to make a system really performant if the api is not optimal.

Quote from: BlueHazzard on July 31, 2017, 09:50:38 PM
Why is this a bad idea?
It creates delays for the user when you need the data. I guess here it won't be that bad and it could be disabled if it creates too many ux problems. I'm not sure I like the similar implementation in the gdb/mi plugin.

Quote from: BlueHazzard on July 31, 2017, 09:50:38 PM
...because from my experience the problem is codeblocks<->gdb and not gdb or codeblocks at program level.
Sometimes gdb is slow, because it has tons of data to process and produces small amount of data.

Quote from: BlueHazzard on July 31, 2017, 09:50:38 PM
In any way it is faster then updating the whole register map (if the map is 11kByte big, on smaller targets it may be a possibility...)
This could be done in the gdb plugin. Because it is its job to iterate the register-watches and decide what commands to issue. It might be possible to merge different memory ranges. If they overlap or next to each other.
(most of the time I ignore long posts)
[strangers don't send me private messages, I'll ignore them; post a topic in the forum, but first read the rules!]

BlueHazzard

I tried to implement the way you suggested. We need to distinguish between a "symbol" watch and a "memory" watch, because the gdb command for asking a symbol is "output" and for a memory region it is "x". Now my first try is to make the distinguish within the watch. For this is have to:
1) Extend the cbWatch class with


        enum cbWatchType
        {
            cbWatchType_SYMBOL,
            cbWatchType_MEMORY
        };

        virtual void AddMemoryRange(uint64_t address, uint64_t size, const wxString &id ) = 0;
        cbWatchType  GetWatchType()     {return m_watchType;};

        private:
            cbWatchType m_watchType;


2) Modify the GDBWatch class:

void GDBWatch::AddMemoryRange(uint64_t address, uint64_t size, const wxString &id )
{
    m_symbol = wxString::Format(wxT("/%ulub %lx"), size, address);
    m_watchType = cbWatchType_MEMORY;
}

void GDBWatch::SetSymbol(const wxString& symbol)
{
    m_symbol = symbol;
    m_watchType = cbWatchType_SYMBOL;
}

now my plan was to modify the class GdbCmd_Watch to distinguish between memory and symbol watch

        GdbCmd_Watch(DebuggerDriver* driver, cb::shared_ptr<GDBWatch> watch) :
            DebuggerCmd(driver),
            m_watch(watch)
        {
            wxString type;
            wxString symbol;

            m_watch->GetSymbol(symbol);
            m_watch->GetType(type);

            if(m_watch->GetWatchType == cbWatchType_MEMORY)
            {
                m_Cmd << "x " << symbol;
                return;
            }
// old code from here on

            type.Trim(true);
            type.Trim(false);
            m_Cmd = static_cast<GDB_driver*>(m_pDriver)->GetScriptedTypeCommand(type, m_ParseFunc);
            if (m_Cmd.IsEmpty())
            {

Now i wanted to modify the
ParseGDBWatchValue
function to parse a memory and symbol watch differently. But it is not that easy....
There is also the GdbCmd_FindWatchType command. I have discovered that the process for updating a watch is to
1) Queue a GdbCmd_FindWatchType command.
2) After successful action this command queues a GdbCmd_Watch
Now the GdbCmd_FindWatchType is useless for the memory watch. So i have to insert even more logic, or modify it future up...

Is this really the way you want to go?

oBFusCATed

Why do you want to reuse the watch parsing and querying in the gdb plugin?
When adding memory watches you can add them to a separate array of objects and treat them differently inside the plugin.
The idea is to reuse the interface of cbWatch, but not the implementation inside the plugin.

I don't see why the cbWatch needs to have a type at this point in time of development.
Probably when you start to do the UI you'll have to add some field that is used to specify the widget and some what to get the data for the widget, but you are not at this step yet.

p.p. C::B is using C++11 now, so you can take advantage of the better enums and don't have to explicitly add namespace.
(most of the time I ignore long posts)
[strangers don't send me private messages, I'll ignore them; post a topic in the forum, but first read the rules!]

BlueHazzard

ok, i have now implemented a first version of the whole thing:
https://youtu.be/ELOoFKLemmQ

i will upload the source code to github as soon as possible...

BlueHazzard


oBFusCATed

DeleteMemoryRange and HasMemoryRange seem redundant...
Also I'm not sure it is a good idea to use GDBWatch as a class and to store the command in the symbol field or the result as a string in the value.
(most of the time I ignore long posts)
[strangers don't send me private messages, I'll ignore them; post a topic in the forum, but first read the rules!]

BlueHazzard

QuoteDeleteMemoryRange and HasMemoryRange seem redundant...
i agree with that.. This popped up my mind as soon as i pushed the code

QuoteAlso I'm not sure it is a good idea to use GDBWatch as a class and to store the command in the symbol field or the result as a string in the value.
This could easily be made, and honestly i would prefer it. But the result has to be stored in the value member as string, because otherwise the cbWatch has to be modified, or how do you think to get the result from outside of the gdb plugin? One possibility would be to add a function
bool GetMemoryRangeValue(cb:shared_ptr<cbWatch> watch, std::vector<char> &data)
to cbDebuggerPlugin, but this seems not intuitive if the normal use of cbWatch is GetValue().

oBFusCATed

Quote from: BlueHazzard on August 08, 2017, 10:28:48 AM
This could easily be made, and honestly i would prefer it. But the result has to be stored in the value member as string, because otherwise the cbWatch has to be modified, or how do you think to get the result from outside of the gdb plugin?
It depends what you want to do with the data. Do you know what operations you want to do?

Quote from: BlueHazzard on August 08, 2017, 10:28:48 AM
One possibility would be to add a function
bool GetMemoryRangeValue(cb:shared_ptr<cbWatch> watch, std::vector<char> &data)
to cbDebuggerPlugin, but this seems not intuitive if the normal use of cbWatch is GetValue().
Do not go this route, please.
(most of the time I ignore long posts)
[strangers don't send me private messages, I'll ignore them; post a topic in the forum, but first read the rules!]

BlueHazzard

Hi,
i reworked the implementation: https://github.com/bluehazzard/codeblocks_sf/tree/debugger/memory_range_watch

The interface to all plugins is only one function:

cb::shared_ptr<cbWatch>  cbDebuggerPlugin::AddMemoryRange(uint64_t address, uint64_t size, const wxString &id ) = 0;

This function returns a cbWatch object that represents a memory range. The value of the Watch (the memory raw data directly from ram) can be get with (pseudo code):

cb::shared_ptr<cbWatch> m_watch = dbg_plugin->AddMemoryRange(0x400000, 16, wxEmtyString );
// Run the debugger
wxString tmp;
m_watch->GetValue(tmp);
size_t lengthOfData = tmp.size();
char* memoryContetn = new char[ lengthOfData ];
memcpy(memoryContent, tmp.To8BitData(), 16);
// yay: ram content in memoryContent for your free use and interpretation
// i know there is a uint16_t @ 0x400002
uint16_t myNeededValue = 0;
memcpy(&myNeededValue, memoryContent+2, 2);


At plugin level there is a internal watch class: GDBMemoryRangeWatch that represents a memory range. It derives from cbWatch and stores the address and size of the range.
All the memory range watches are stored in

std::vector<cb::shared_ptr<GDBMemoryRangeWatch> > m_memoryRange;

and the following functions are transparent for normal and memory range watches:

void DebuggerGDB::DeleteWatch(cb::shared_ptr<cbWatch> watch)
bool DebuggerGDB::HasWatch(cb::shared_ptr<cbWatch> watch)

there is an additional function (at the moment only for GDBPlugin but it can be added to cbDebuggerPlugin) to check if the watch is a memory range watch

bool DebuggerGDB::IsMemoryRangeWatch(cb::shared_ptr<cbWatch> watch)


The actual reading of the memory from gdb is implemented like the normal watches with a
class GdbCmd_MemoryRangeWatch : public DebuggerCmd


This implementation works nice and transparent. It is relatively fast and i use it in the new plugin i showed with the video above. I will post the link to the source later tody...

conclusion:
To add a interface for other plugins to read random ram content from the debugger, only one new function has to be added to cbDebuggerPlugin. The data is exchanged through wxString::To8BitData() so the cbWatch class has not to be altered.

@oBFusCATed: Is this interface somehow acceptable for you?

BlueHazzard

Ok, this is my suggestion for a pull request. I think the commits are not to large and quite obvious...

https://github.com/bluehazzard/codeblocks_sf/tree/debugger/pull_candidate/memory_range_watch/1

any thoughts on this?

oBFusCATed

(most of the time I ignore long posts)
[strangers don't send me private messages, I'll ignore them; post a topic in the forum, but first read the rules!]