Author Topic: Redundant Crash in CCManager::OnShowCallTip()  (Read 15158 times)

Offline darksquall57

  • Multiple posting newcomer
  • *
  • Posts: 23
Redundant Crash in CCManager::OnShowCallTip()
« on: August 07, 2015, 10:01:44 am »
Hi,

I'm working with codeblocks for a long time now, and sometimes I have some crashes and if I restart codeblocks it's ok.
But this time it's not the case, I have a crash which occurs after just a few actions ( moving in the code, writing a function call, hitting ";" to finish my line, ... ).

I cannot share my code, but here is the call I retrieved from the Ubuntu crash report.

Code
  <stack>
    <frame level="0"/>
    <frame level="1" function="wxStringBase::operator=(wxStringBase const&amp;)" offset="00000012"/>
    <frame level="2" function="CCManager::OnShowCallTip(CodeBlocksEvent&amp;)" offset="0000017a"/>
    <frame level="3" function="Manager::ProcessEvent(CodeBlocksEvent&amp;)" offset="00000080"/>
    <frame level="4" function="CCManager::OnDeferredCallTipShow(wxCommandEvent&amp;)" offset="000000a3"/>
    <frame level="5" function="wxEvtHandler::ProcessEventIfMatches(wxEventTableEntryBase const&amp;, wxEvtHandler*, wxEvent&amp;)" offset="00000056"/>
    <frame level="6" function="wxEvtHandler::SearchDynamicEventTable(wxEvent&amp;)" offset="0000004f"/>
    <frame level="7" function="wxEvtHandler::ProcessEvent(wxEvent&amp;)" offset="00000092"/>
    <frame level="8" function="wxEvtHandler::ProcessPendingEvents()" offset="00000068"/>
    <frame level="9" function="wxAppConsole::ProcessPendingEvents()" offset="00000051"/>
    <frame level="10" function="wxAppBase::ProcessIdle()" offset="0000001e"/>
    <frame level="11"/>
    <frame level="12" function="g_main_context_dispatch" offset="00000135"/>
    <frame level="13"/>
    <frame level="14" function="g_main_loop_run" offset="0000006a"/>
    <frame level="15" function="gtk_main" offset="000000a7"/>
    <frame level="16" function="wxEventLoop::Run()" offset="0000003a"/>
    <frame level="17" function="wxAppBase::MainLoop()" offset="0000004c"/>
  </stack>

I hope this will help you to find it :D

Offline ollydbg

  • Developer
  • Lives here!
  • *****
  • Posts: 6077
  • OpenCV and Robotics
    • Chinese OpenCV forum moderator
Re: Redundant Crash in CCManager::OnShowCallTip()
« Reply #1 on: August 07, 2015, 02:17:54 pm »
Thanks fore the report.

Indeed, we have discussion about this issue before. That's the reason why CCManager::OnDeferredCallTipShow() was introduced, but it looks like we haven't really fix the real problem yet.

One possible reason is that wxString(in wx2.8.12) is not thread safe.
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.

Offline oBFusCATed

  • Developer
  • Lives here!
  • *****
  • Posts: 13406
    • Travis build status
Re: Redundant Crash in CCManager::OnShowCallTip()
« Reply #2 on: August 08, 2015, 12:20:58 am »
Can you please install the debug packages (-dbg).
Start codeblocks under gdb (gdb /usr/bin/codeblocks) and when it crashes please run the "thread apply all bt" in the gdb command prompt.

If you can reproduce this in a simple project or some big opensource project it will be invaluable.
(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!]

Offline ollydbg

  • Developer
  • Lives here!
  • *****
  • Posts: 6077
  • OpenCV and Robotics
    • Chinese OpenCV forum moderator
Re: Redundant Crash in CCManager::OnShowCallTip()
« Reply #3 on: August 09, 2015, 02:26:30 pm »
@darksquall57, can you try to build CB against wx3.x, which has better thread safe wxString implementation. 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.

Offline darksquall57

  • Multiple posting newcomer
  • *
  • Posts: 23
Re: Redundant Crash in CCManager::OnShowCallTip()
« Reply #4 on: August 11, 2015, 09:33:39 am »
Can you please install the debug packages (-dbg).

you mean calling "./configure --enable-debug" ?

If you can reproduce this in a simple project or some big opensource project it will be invaluable.
Sadly, I can't reproduce it for now, but I'm working on it.

@darksquall57, can you try to build CB against wx3.x, which has better thread safe wxString implementation. Thanks.
I'll try that too, thanks.

Offline oBFusCATed

  • Developer
  • Lives here!
  • *****
  • Posts: 13406
    • Travis build status
Re: Redundant Crash in CCManager::OnShowCallTip()
« Reply #5 on: August 11, 2015, 10:19:20 am »
you mean calling "./configure --enable-debug" ?
If you're building cb yourself then yes.
If you're using binary packages then there is a package that has debug info in it. You have to just install it.
(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!]

Offline ollydbg

  • Developer
  • Lives here!
  • *****
  • Posts: 6077
  • OpenCV and Robotics
    • Chinese OpenCV forum moderator
Re: Redundant Crash in CCManager::OnShowCallTip()
« Reply #6 on: August 11, 2015, 02:30:11 pm »
About the crash issue: I firstly guess this is the thread issue, but when I check the code, I see that in ccmanager
Code
void CCManager::OnShowCallTip(CodeBlocksEvent& event)
{
    event.Skip();

    int tooltipMode = Manager::Get()->GetConfigManager(wxT("ccmanager"))->ReadInt(wxT("/tooltip_mode"), 1);
    // 0 - disable
    // 1 - enable
    // 2 - force single page
    // 3 - keybound only
    if (tooltipMode == 0)
        return;

    cbEditor* ed = Manager::Get()->GetEditorManager()->GetBuiltinActiveEditor();
    if (!ed)
        return;

    cbCodeCompletionPlugin* ccPlugin = GetProviderFor(ed);
    if (!ccPlugin)
        return;

    cbStyledTextCtrl* stc = ed->GetControl();
    if (!stc)
        return;

    int pos = stc->GetCurrentPos();
    int argsPos = wxSCI_INVALID_POSITION;
    wxString curTip;
    if (!m_CallTips.empty())
        curTip = m_CurCallTip->tip;
    m_CallTips = ccPlugin->GetCallTips(pos, stc->GetStyleAt(pos), ed, argsPos);
    ...
When call the ccPlugin's GetCallTips() function, I see that there is a check:
Code
std::vector<CodeCompletion::CCCallTip> CodeCompletion::GetCallTips(int pos, int style, cbEditor* ed, int& argsPos)
{
    std::vector<CCCallTip> tips;
    if (!IsAttached() || !m_InitDone || style == wxSCI_C_WXSMITH || !m_NativeParser.GetParser().Done())
        return tips;

    int typedCommas = 0;
    wxArrayString items;
    ...
You see, there is a check "!m_NativeParser.GetParser().Done()", This means there is no other thread running on the TokenTree(At least in the active Parser object), so my question is: do you open several cbp projects in C::B? This is the chance that other Parser objects are running parsing jobs in its thread pool.

Another guess: as Morten has once said the crash line is here
Code
curTip = m_CurCallTip->tip;
My guess is that if m_CurCallTip(an iterator) could point to wrong elements of the m_CallTips(vector)?
EDIT: Maybe, you have two ccPlugins, and with one plugin, you have update the iterator, but this iterator point to the vector updated by another ccPlugin. (This is just a guess)

Since I don't have a chance to reproduce this crash issue, I have all my guesses by reading the CC and CCmanager's source code.



 
« Last Edit: August 11, 2015, 02:44:37 pm by ollydbg »
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.

Offline ollydbg

  • Developer
  • Lives here!
  • *****
  • Posts: 6077
  • OpenCV and Robotics
    • Chinese OpenCV forum moderator
Re: Redundant Crash in CCManager::OnShowCallTip()
« Reply #7 on: August 11, 2015, 03:00:41 pm »
@Alpha, can you explain what the meaning of  "// search long term recall" and "// search short term recall" in the function void CCManager::OnShowCallTip(CodeBlocksEvent& event)?

I can't quite understand the related code snippet. Since you have some directory, and hash code in those code.

My guess is that you want to enable a cache?  For example:

Code
void f(int a, float b, double c);  
void f(char d, void *h);

When user select a call tip of function "void f(char d, void *h);" in the call tip window, the next time when the call tip window is shown, the "void f(char d, void *h);"  is automatically selected?

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.

Offline ollydbg

  • Developer
  • Lives here!
  • *****
  • Posts: 6077
  • OpenCV and Robotics
    • Chinese OpenCV forum moderator
Re: Redundant Crash in CCManager::OnShowCallTip()
« Reply #8 on: August 12, 2015, 03:29:44 am »
I found that one issue is that sometimes, the tip window doesn't cancel correctly.

Code
void cbStyledTextCtrl::CallTipCancel()
{
    if (!m_tabSmartJump)
        wxScintilla::CallTipCancel();
}
This is called inside the DoShowTips() function.
Code
void CCManager::DoShowTips(const wxStringVec& tips, cbStyledTextCtrl* stc, int pos, int argsPos, int hlStart, int hlEnd)
{
    ...
    if (stc->CallTipActive() && m_LastTipPos != pos)
        stc->CallTipCancel(); // force tip popup to invalidate (sometimes fails to otherwise do so on Windows)
    stc->CallTipShow(pos, tip);
    if (hlStart >= 0 && hlEnd > hlStart)
        stc->CallTipSetHighlight(hlStart, hlEnd);
    m_LastTipPos = pos;
}

stc->CallTipCancel();  won't actually cancel the calltip, because the m_tabSmartJump==true.
Later, we call stc->CallTipShow() again.

EDIT: I see that in this case, another calltip is allocated from the wxScintilla..... So, how about the first calltip?
« Last Edit: August 12, 2015, 03:39:57 am by ollydbg »
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.

Offline ollydbg

  • Developer
  • Lives here!
  • *****
  • Posts: 6077
  • OpenCV and Robotics
    • Chinese OpenCV forum moderator
Re: Redundant Crash in CCManager::OnShowCallTip()
« Reply #9 on: August 12, 2015, 04:10:58 am »
Oh, I see some code snippet:
Code
// cbEVT_EDITOR_TOOLTIP
void CCManager::OnEditorTooltip(CodeBlocksEvent& event)
{
    ...
    if (!ccPlugin || pos < 0 || pos >= stc->GetLength())
    {
        if (stc->CallTipActive() && event.GetExtraLong() == 0 && m_CallTipActive == wxSCI_INVALID_POSITION)
            static_cast<wxScintilla*>(stc)->CallTipCancel();
        return;
    }
Here, why you use the static_cast<wxScintilla*>(stc)->CallTipCancel();? You want to skip the condition check on m_tabSmartJump? and force to cancel it?
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.

Offline Alpha

  • Developer
  • Lives here!
  • *****
  • Posts: 1513
Re: Redundant Crash in CCManager::OnShowCallTip()
« Reply #10 on: August 12, 2015, 06:13:48 am »
Here, why you use the static_cast<wxScintilla*>(stc)->CallTipCancel();? You want to skip the condition check on m_tabSmartJump? and force to cancel it?
Yes; calling base class to force it to cancel.

@Alpha, can you explain what the meaning of  "// search long term recall" and "// search short term recall" in the function void CCManager::OnShowCallTip(CodeBlocksEvent& event)?

I can't quite understand the related code snippet. Since you have some directory, and hash code in those code.
They are remember which page on the calltip was last selected, and show that again first next time it is requested.
Short term recall caches the current calltip, so it can be displayed exactly if a calltip is re-requested in the same location.
Long term recall uses two dictionaries, the first one based on the tip's content, and will display the last shown page when a new calltip is requested that has the same content.  The second (fuzzy) dictionary remembers which page was shown based on the typed word prefix (this is to support FortranProject, which uses more dynamic calltips).

Offline ollydbg

  • Developer
  • Lives here!
  • *****
  • Posts: 6077
  • OpenCV and Robotics
    • Chinese OpenCV forum moderator
Re: Redundant Crash in CCManager::OnShowCallTip()
« Reply #11 on: August 12, 2015, 07:22:48 am »
Here, why you use the static_cast<wxScintilla*>(stc)->CallTipCancel();? You want to skip the condition check on m_tabSmartJump? and force to cancel it?
Yes; calling base class to force it to cancel.
OK, thanks, then do we use the wxScintilla::CanTipCancel() in the void CCManager::OnEditorTooltip()?

Quote
@Alpha, can you explain what the meaning of  "// search long term recall" and "// search short term recall" in the function void CCManager::OnShowCallTip(CodeBlocksEvent& event)?

I can't quite understand the related code snippet. Since you have some directory, and hash code in those code.
They are remember which page on the calltip was last selected, and show that again first next time it is requested.
Short term recall caches the current calltip, so it can be displayed exactly if a calltip is re-requested in the same location.
Long term recall uses two dictionaries, the first one based on the tip's content, and will display the last shown page when a new calltip is requested that has the same content.  The second (fuzzy) dictionary remembers which page was shown based on the typed word prefix (this is to support FortranProject, which uses more dynamic calltips).
Thanks very much. I briefly understand this.
I'm currently add those text to the comment part in ccmanager.h/cpp files  :)
Feel free to adjust.
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.

Offline darksquall57

  • Multiple posting newcomer
  • *
  • Posts: 23
Re: Redundant Crash in CCManager::OnShowCallTip()
« Reply #12 on: August 12, 2015, 11:10:01 am »
Wow ! A lot of work and discussion since my last post :D

You see, there is a check "!m_NativeParser.GetParser().Done()", This means there is no other thread running on the TokenTree(At least in the active Parser object), so my question is: do you open several cbp projects in C::B?

I'm working only with 1 project opened at the same time.

EDIT: Maybe, you have two ccPlugins, and with one plugin, you have update the iterator, but this iterator point to the vector updated by another ccPlugin. (This is just a guess)

I don't know, how can I figure this out ?

Offline ollydbg

  • Developer
  • Lives here!
  • *****
  • Posts: 6077
  • OpenCV and Robotics
    • Chinese OpenCV forum moderator
Re: Redundant Crash in CCManager::OnShowCallTip()
« Reply #13 on: August 12, 2015, 02:24:59 pm »

You see, there is a check "!m_NativeParser.GetParser().Done()", This means there is no other thread running on the TokenTree(At least in the active Parser object), so my question is: do you open several cbp projects in C::B?

I'm working only with 1 project opened at the same time.

OK, so even a single project could cause the crash bug.

Quote
EDIT: Maybe, you have two ccPlugins, and with one plugin, you have update the iterator, but this iterator point to the vector updated by another ccPlugin. (This is just a guess)

I don't know, how can I figure this out ?
The Fortran code completion plugin is a SVN external module of our C::B's SVN repo, you can see its source in "\src\plugins\contrib\FortranProject" if you use SVN. If you have already build this plugin, you can disable it from the Plugin manager in the Menu->Plugins->Manager plugins.
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.

Offline ollydbg

  • Developer
  • Lives here!
  • *****
  • Posts: 6077
  • OpenCV and Robotics
    • Chinese OpenCV forum moderator
Re: Redundant Crash in CCManager::OnShowCallTip()
« Reply #14 on: August 13, 2015, 04:23:57 pm »
I think I may find the reason why the crash happens by reading the source code.  ;)

We have such code:
Code
void CCManager::OnShowCallTip(CodeBlocksEvent& event)
{
    event.Skip();

    int tooltipMode = Manager::Get()->GetConfigManager(wxT("ccmanager"))->ReadInt(wxT("/tooltip_mode"), 1);
    // 0 - disable
    // 1 - enable
    // 2 - force single page
    // 3 - keybound only
    if (tooltipMode == 0)
        return;

    cbEditor* ed = Manager::Get()->GetEditorManager()->GetBuiltinActiveEditor();
    if (!ed)
        return;

    cbCodeCompletionPlugin* ccPlugin = GetProviderFor(ed);
    if (!ccPlugin)
        return;

    cbStyledTextCtrl* stc = ed->GetControl();
    if (!stc)
        return;

    int pos = stc->GetCurrentPos();
    int argsPos = wxSCI_INVALID_POSITION;
    // save the current tip shown text for later recalling
    wxString curTip;
    if (!m_CallTips.empty())
        curTip = m_CurCallTip->tip;
    m_CallTips = ccPlugin->GetCallTips(pos, stc->GetStyleAt(pos), ed, argsPos);
    // since m_CallTips get updated, we should update the m_CurCallTip
    // But what about if m_CallTips is empty?

    if (!m_CallTips.empty() && (event.GetInt() != FROM_TIMER || argsPos == m_CallTipActive))
    {
        ...
        Here, we update the m_CurCallTip
        ...
    }
Please see my comments
Code
    // since m_CallTips get updated, we should update the m_CurCallTip
    // But what about if m_CallTips is empty?
I mean, if m_CallTips is modified, we should update the m_CurCallTip, since m_CurCallTip is an iterator points to the m_CallTips. If m_CallTips is empty, we have no chance to update the m_CurCallTip, then m_CurCallTip becomes a wild pointer.
Am I right? I haven't tested and debug the code, I just read the cc related code time and time again. ;)
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.