News:

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

Main Menu

Some thoughts on doing breakpoints persistent

Started by ollydbg, June 16, 2009, 11:18:18 AM

Previous topic - Next topic

ollydbg

#15
Quote from: oBFusCATed on June 16, 2009, 06:06:08 PM
We talk about data per project.
If a plugin needs some global configuration, the core can(must) still provide an interface for storing settings.

Yes, There are many *.ini files  for plugin global settings in APPDATA folder.

Edit:
Some global settings were using XML format, such as "default.conf"
Others use INI file format.


For example the BrowseTracker.ini

BrowseMarksEnabled=1
BrowseMarksStyle=0
BrowseMarksToggleKey=0
LeftMouseDelay=400
BrowseMarksClearAllMethod=0


I'm not sure the core supply method to access these ini files.


Edit2

I found in cbproject.cpp, there are some code do the "your_project_name.layout" file IO.


bool cbProject::SaveLayout()
{
    if (m_Filename.IsEmpty())
        return false;

    wxFileName fname(m_Filename);
    fname.SetExt(_T("layout"));
    ProjectLayoutLoader loader(this);
    return loader.Save(fname.GetFullPath());
}

bool cbProject::LoadLayout()
{
   if (m_Filename.IsEmpty())
        return false;
    int openmode = Manager::Get()->GetConfigManager(_T("project_manager"))->ReadInt(_T("/open_files"), (long int)1);
    bool result = false;

    if(openmode==2)
    {
        // Do not open any files
        result = true;
    }
    else
    {
        Manager::Get()->GetEditorManager()->HideNotebook();
        if(openmode == 0) // Open all files
        {
            FilesList::Node* node = m_Files.GetFirst();
            while(node)
            {
                ProjectFile* f = node->GetData();
                Manager::Get()->GetEditorManager()->Open(f->file.GetFullPath(),0,f);
                node = node->GetNext();
            }
            result = true;
        }
        else if(openmode == 1)// Open last open files
        {
            wxFileName fname(m_Filename);
            fname.SetExt(_T("layout"));
            ProjectLayoutLoader loader(this);
            if (loader.Open(fname.GetFullPath()))
            {
                typedef std::map<int, ProjectFile*> open_files_map;
                open_files_map open_files;

                // Get all files to open and sort them according to their tab-position:
                FilesList::Node* node = m_Files.GetFirst();
                while(node)
                {
                    ProjectFile* f = node->GetData();
                    if (f->editorOpen)
                        open_files[f->editorTabPos] = f;
                    node = node->GetNext();
                }

                // Load all requested files
                std::vector<LoaderBase*> filesInMemory;
                for (open_files_map::iterator it = open_files.begin(); it != open_files.end(); ++it)
                {
                    filesInMemory.push_back(Manager::Get()->GetFileManager()->Load((*it).second->file.GetFullPath()));
                }
                // Open all requested files:
                size_t i = 0;
                for (open_files_map::iterator it = open_files.begin(); it != open_files.end(); ++it)
                {
                    cbEditor* ed = Manager::Get()->GetEditorManager()->Open(filesInMemory[i], (*it).second->file.GetFullPath(),0,(*it).second);
                    if (ed)
                        ed->SetProjectFile((*it).second);
                    ++i;
                }

                ProjectFile* f = loader.GetTopProjectFile();
                if (f)
                {
                    Manager::Get()->GetLogManager()->DebugLog(_T("Top Editor: ") + f->file.GetFullPath());
                    EditorBase* eb = Manager::Get()->GetEditorManager()->Open(f->file.GetFullPath());
                    if(eb)
                        eb->Activate();
                }
//                Manager::Get()->GetAppWindow()->Thaw();
            }
            result = true;
        }
        else
            result = false;
        Manager::Get()->GetEditorManager()->ShowNotebook();
    }
    return result;
}
If some piece of memory should be reused, turn them to variables (or const variables).
If some piece of operations should be reused, turn them to functions.
If they happened together, then turn them to classes.

ollydbg

Hi, I'm doing a simple test.
Here is a breakpoints layout file

<?xml version="1.0" encoding="UTF-8" standalone="yes" ?>
<BreakPoints_layout_file>
<BreakPoint file="C:/test/test_for_cb_forum/main.cpp" position="58" />
<BreakPoint file="C:/test/test_for_cb_forum/main.cpp" position="74" />
<BreakPoint file="C:/test/test_for_cb_forum/main.cpp" position="71" />
<BreakPoint file="C:/test/test_for_cb_forum/main.cpp" position="68" />
<BreakPoint file="C:/test/test_for_cb_forum/main.cpp" position="66" />
</BreakPoints_layout_file>


My question is:
Is there any way I can change the absolute file name to relative file name?

Since my code is like this:


// ----------------------------------------------------------------------------
bool BreakPointsLayout::Save(const wxString& filename, BreakpointsList &breakPointsList)
// ----------------------------------------------------------------------------
{
    const char* ROOT_TAG = "BreakPoints_layout_file";

    TiXmlDocument doc;
    doc.SetCondenseWhiteSpace(false);
    doc.InsertEndChild(TiXmlDeclaration("1.0", "UTF-8", "yes"));
    TiXmlElement* rootnode = static_cast<TiXmlElement*>(doc.InsertEndChild(TiXmlElement(ROOT_TAG)));
    if (!rootnode)
        return false;


    for (int i = breakPointsList.GetCount() - 1; i >= 0; --i)
    {
        DebuggerBreakpoint* bp = breakPointsList[i];
        //Only save the breakpoints belong to the current project
        if (bp->userData == m_pProject)
        {
            Manager::Get()->GetLogManager()->DebugLog(F(_T("Got one")));
            TiXmlElement* node = static_cast<TiXmlElement*>(rootnode->InsertEndChild(TiXmlElement("BreakPoint")));
            node->SetAttribute("file", cbU2C(bp->filename));
            node->SetAttribute("position", bp->line);
            //RemoveBreakpoint(i, true);
        }
    }


    return cbSaveTinyXMLDocument(&doc, filename);
}


bp->filename is a full name.

But in debugger_def.h


struct DebuggerBreakpoint
{
enum BreakpointType
{
bptCode = 0, ///< Normal file/line breakpoint
bptFunction, ///< Function signature breakpoint
bptData ///< Data breakpoint
};

    /** Constructor.
      * Sets default values for members.
      */
    DebuggerBreakpoint()
        : type(bptCode),
        line(0),
        index(-1),
        temporary(false),
        enabled(true),
        active(true),
        useIgnoreCount(false),
        ignoreCount(0),
        useCondition(false),
        wantsCondition(false),
        address(0),
        alreadySet(false),
        breakOnRead(false),
        breakOnWrite(true),
        userData(0)
    {}
    BreakpointType type; ///< The type of this breakpoint.
    wxString filename; ///< The filename for the breakpoint (kept as relative).


It said the file name should  keep as relative. :(


Any Comments?

Thanks
If some piece of memory should be reused, turn them to variables (or const variables).
If some piece of operations should be reused, turn them to functions.
If they happened together, then turn them to classes.

Jenna

You can use the MakeRelativeTo-function of wxFileName.

The code below is not tested !

    wxFileName fname;

    for (int i = breakPointsList.GetCount() - 1; i >= 0; --i)
    {
        DebuggerBreakpoint* bp = breakPointsList[i];
        //Only save the breakpoints belong to the current project
        if (bp->userData == m_pProject)
        {
            Manager::Get()->GetLogManager()->DebugLog(F(_T("Got one")));
            TiXmlElement* node = static_cast<TiXmlElement*>(rootnode->InsertEndChild(TiXmlElement("BreakPoint")));
            fname.Assign(bp->filename);
            if (fname.IsAbsolute())
            {
                fname.MakeRelativeTo(m_pProject->GetBasePath());
            }
            node->SetAttribute("file", cbU2C(fname.GetFullPath()));
            node->SetAttribute("position", bp->line);
            //RemoveBreakpoint(i, true);
        }
    }


Instead of m_pProject->GetBasePath() it might also make sense to use m_pProject->GetCommonTopLevelPath().
Depends on what the debugger-plugin wants.

No time to look into it at themoment.

ollydbg

Thanks for your help. Now, using the MakeRelativeTo() function ,I can save and load the saved breakpoints( simple position based breakpoints)

There is only one thing left. Since when I load the project, the BreakpointsList were successfully loaded from a projectname.bps file. But the UI has not notified. See the image below:


Breakpoints on Line 77 73 and 71 were loaded, but they were not shown on the editor. Even I add another breakpoint on line 59.

Then only way to show the breakpoints icon in the editor is "reopen the main.cpp file", see below:



So, I need a way to "notify the editor" after all the breakpoints were loaded.

If some piece of memory should be reused, turn them to variables (or const variables).
If some piece of operations should be reused, turn them to functions.
If they happened together, then turn them to classes.

Jenna

Do you use AddBreakpoint from debugger-plugin or from editor ?

You can use the function from cbEditor, if the source-file is already opened and set notifyDebugger to true or use the function from debugger-plugin, if the file is not (yet) opened.

ollydbg

#20
Quote from: jens on June 17, 2009, 01:23:10 PM
Do you use AddBreakpoint from debugger-plugin or from editor ?

You can use the function from cbEditor, if the source-file is already opened and set notifyDebugger to true or use the function from debugger-plugin, if the file is not (yet) opened.

I totally use the function AddBreakpoint from debuggergdb plugin  debuggerstate.

I will try it now, Thanks very much!
If some piece of memory should be reused, turn them to variables (or const variables).
If some piece of operations should be reused, turn them to functions.
If they happened together, then turn them to classes.

ollydbg

#21
Sorry, I can't figure how to check if the current file was opened in cbEditor. Can someone help me?

here is the patch.( The patch has include some patches from oBFusCATed adding a command entry)


http://sites.google.com/site/studycodeblocks/imagescb/breakpoints.patch This patch has been updated in

https://developer.berlios.de/patch/index.php?func=detailpatch&patch_id=2775&group_id=5358


Here is some explanation about this patch:

1, I add two files: breakpointslayout.cpp and breakpointslayout.h(They are similar with code in BrowserTracker plugin)

2, I add some code in doing DebuggerGDB::OnProjectOpened and DebuggerGDB::OnProjectClosed

3, When a project is opened, I will use Addbreakpoints function in debuggerstate structure.

4, when a project is closed, I just read the breakpointsList in debuggerstate, then save them to a xml file.

5, I still can't find a way to notify cbEditor, so, the breakpoints add silently. :(



By the way, I can't upload an attachment any more(upload folder is FULL), can someone help me?  :(

Thanks

Edit:

Here are some ideas:

class ProjectFile  : public BlockAllocated<ProjectFile, 1000>
{
......
       /** If true, the file is open inside an editor. */
       bool editorOpen; // layout info



Can we use "ProjectFile" related method?
If some piece of memory should be reused, turn them to variables (or const variables).
If some piece of operations should be reused, turn them to functions.
If they happened together, then turn them to classes.

Jenna

You can try:
EditorBase* eb = Manager::Get()->GetEditorManager()->IsOpen(filename);
or
cbEditor* ed = Manager::Get()->GetEditorManager()->IsBuiltinOpen( filename );

Both return NULL if not opened.

ollydbg

#23
@jens
Thanks very much! I will try it....So excited!!!

Edit:

Problem solved!!!! Works perfectly! Just change this function to:


// ----------------------------------------------------------------------------
bool BreakpointsLayout::Load(const wxString& filename, DebuggerState * pDebuggerState)
// ----------------------------------------------------------------------------
{
   TiXmlDocument doc;
   if (!TinyXML::LoadDocument(filename, &doc))
       return false;

   TiXmlElement* root;
   TiXmlElement* elem;
   wxString fname;
   ProjectFile* pf;


   root = doc.FirstChildElement("Breakpoints_layout_file");
   if (!root)
   {
       return false;
   }

   elem = root->FirstChildElement("Breakpoint");

   while (elem)
   {
       wxString fname = cbC2U(elem->Attribute("file"));
       ProjectFile* pf;
       if (fname.IsEmpty())
       {
           break;
       }
       else
           pf = m_pProject->GetFileByFilename(fname);

       wxString filenamePath = pf->file.GetFullPath();

       int line = 0;
       if (not elem->QueryIntAttribute("position", &line) == TIXML_SUCCESS)
           break;

       //Manager::Get()->GetLogManager()->DebugLog(F(_T("file='%s', line='%d'"), filenamePath.c_str(), line));


       cbEditor* ed = Manager::Get()->GetEditorManager()->IsBuiltinOpen( filenamePath );
       if (ed==NULL){
           pDebuggerState->AddBreakpoint(filenamePath,line); //add breakpoints silently
           Manager::Get()->GetLogManager()->DebugLog(F(_T("silently add bp file='%s', line='%d'"), filenamePath.c_str(), line));
       }
       else{
           ed->AddBreakpoint(line);
           Manager::Get()->GetLogManager()->DebugLog(F(_T("add bp from editor file='%s', line='%d'"), filenamePath.c_str(), line));

       }
       elem = elem->NextSiblingElement();
   }
   return true;
}//Load




If some piece of memory should be reused, turn them to variables (or const variables).
If some piece of operations should be reused, turn them to functions.
If they happened together, then turn them to classes.

ollydbg

Hi, every one.
I have finished the finally patch.

Both watches and breakpoints can be persistent with the current project.

I add the patch here:

https://developer.berlios.de/patch/index.php?func=detailpatch&patch_id=2775&group_id=5358

Here is the example of a projectname.bps file


<?xml version="1.0" encoding="UTF-8" standalone="yes" ?>
<Debugger_layout_file>
<BreakpointsList>
<Breakpoint file="main.cpp" position="67" />
<Breakpoint file="main.cpp" position="69" />
<Breakpoint file="main.cpp" position="62" />
<Breakpoint file="main.cpp" position="58" />
</BreakpointsList>
<WatchesList>
<Watch variable="abcdefg" />
<Watch variable="llll" />
</WatchesList>
</Debugger_layout_file>



Any comments and enhancements are welcome!!

Thanks. :D
If some piece of memory should be reused, turn them to variables (or const variables).
If some piece of operations should be reused, turn them to functions.
If they happened together, then turn them to classes.

Jenna

I did not (yet)test it, but what came in my mind:

what about the other attributes of the breakpoints and watches ?

The breakpoints might be data, function or conditional breakpoints (might be more possibilities, I don't know at the moment).
The same is for watches: might be watches as character, hex ..., or as array with begin and count.


ollydbg

hehe, this is the first step.

I only add codes in debuggerGDB.cpp. For "watches types" and "breakpoints types", things will be more complicated, so, they should be added to the separate cpp and h files.
If some piece of memory should be reused, turn them to variables (or const variables).
If some piece of operations should be reused, turn them to functions.
If they happened together, then turn them to classes.

MortenMacFly

Quote from: jens on June 20, 2009, 03:44:53 PM
The same is for watches: might be watches as character, hex ..., or as array with begin and count.
Notice that watches can already be saved / loaded (including this info).
Compiler logging: Settings->Compiler & Debugger->tab "Other"->Compiler logging="Full command line"
C::B Manual: [url="https://www.codeblocks.org/docs/main_codeblocks_en.html"]https://www.codeblocks.org/docs/main_codeblocks_en.html[/url]
C::B FAQ: [url="https://wiki.codeblocks.org/index.php?title=FAQ"]https://wiki.codeblocks.org/index.php?title=FAQ[/url]

ollydbg

Quote from: MortenMacFly on June 22, 2009, 06:55:10 AM
Quote from: jens on June 20, 2009, 03:44:53 PM
The same is for watches: might be watches as character, hex ..., or as array with begin and count.
Notice that watches can already be saved / loaded (including this info).

Where can I find the code? (save watches with other info?)

The only code I can find is:

void DebuggerTree::OnSaveWatchFile(wxCommandEvent& event)
{
    // Verify that there ARE watches to save
    size_t wc = m_Watches.GetCount();
    if (wc<1)
    {
        cbMessageBox(_("There are no watches in the list to save."),
                     _("Save Watches"), wxICON_ERROR);
        return;
    }

    wxString fname;
    wxFileDialog dlg (Manager::Get()->GetAppWindow(),
                    _T("Save debugger watch file"),
                    _T(""),
                    _T(""),
                    _T("Watch files (*.watch)|*.watch|Any file (*)|*"),
                    wxFD_SAVE | wxFD_OVERWRITE_PROMPT);
    PlaceWindow(&dlg);
    if (dlg.ShowModal() != wxID_OK)
        return;

    wxTextFile tf(dlg.GetPath());
    bool bSuccess = false;

    // Create() will fail if the file exist -> must use Open() if file exist
    if (tf.Exists())
    {
        bSuccess = tf.Open();
        if (bSuccess) tf.Clear(); // remove old content (if any)
    }
    else
    {
        bSuccess = tf.Create();
    }

    if (bSuccess)
    {
        // iterate over each watch and write them to the file buffer
        for (size_t i = 0; i < wc; ++i)
        {
            Watch& w = m_Watches[i];
            tf.AddLine(w.keyword);
        }
        tf.Write(); // Write buffer to file
        tf.Close(); // release file handle
    }
    else
        Manager::Get()->GetLogManager()->DebugLog(_T("Error opening debugger watch file: ") + fname);
}


Seems no other info was saved :(
If some piece of memory should be reused, turn them to variables (or const variables).
If some piece of operations should be reused, turn them to functions.
If they happened together, then turn them to classes.

ollydbg

Active this thread :)
What should we do if we want to save all the user setting information in one file, those include:
The setting should be one file for each c::b project.
1, project layout (which file of the project should be opened/shown after loading the project), caret position
2, breakpoints
3, watches
4, bookmarks
5, other user setting dedicated to the project file.

We should unify the interface, but I have no idea how to implement this.

The user setting file should be saved/loaded as the same time as "bool cbProject::SaveLayout() and bool cbProject::LoadLayout()".
If some piece of memory should be reused, turn them to variables (or const variables).
If some piece of operations should be reused, turn them to functions.
If they happened together, then turn them to classes.