Call Hierarchy

Call Hierarchy

Rate This
  • Comments 35

Hello everyone,

My name is Raman Sharma and I am a PM on the VC++ IDE team.  I want to talk about a new feature called Call Hierarchy in Visual Studio 10.  As the name suggests, it is used for navigating all functions called from within a particular function and all functions that make calls to a particular function.  Many of you would know of a feature called Call Browser which was present in VC6, VS2005 and VS2008, and provided similar functionality.  The Call Hierarchy feature is the revamped version of Call Browser, better organized and easier to use.

In VS2010, Call Hierarchy has been designed such that it can be a shared feature for all languages i.e. the UI is common and different languages (like C#, C++ etc.) can fill the UI with their own data to address the semantic differences between the languages.  For C#, this will be a new feature (See this post for more details).  For C++, it is just about transitioning from the old Call Browser to Call Hierarchy to take advantage of the new design. 

Let’s look at an example that will illustrate the differences between the old functionality and the new one.  Let’s say you want to see the “calls from” and “calls to” information for two functions at the same time: CImagingView::OnRButtonUp and CImagingView::OnContextMenu

In the previous releases, this would have effectively meant you having to open the following four call browser tool windows:

C++ Call Browser in VS2008

The above model clearly suffers from the following limitations:

-          The “Calls From” and “Calls To” are presented in separate tool windows. (windows 1&3 for Calls To and 2&4 for Calls From)

-          Each tool window can hold the call information for only one function at a time.

-          The manner in which results are presented is not very intuitive:

o   In the window 2 above, the names of the functions called from CImagingView::OnContextMenu are listed at the bottom of the tree, whereas the actual calls to those functions are listed under the folder named Calls from ‘OnContextMenu’.

o   There is no link between the above two making navigation harder.

Let me just add, those windows would not be so neatly stacked together as I make them appear in the above screenshot.

In VS2010 however, with the new Call Hierarchy feature, the same information is presented in one tool window in the following form:

C++ Call Hierarchy in VS2010

Notice the following salient features in the above window:

-          “Calls From” and “Calls To” trees for any function appear in the same place in one window.

-          There can be multiple root nodes, thus obviating the need to open a separate window for each function.

o   The user can also right click on any non-root node in the tree and add it as a new root node to achieve better clarity.

-          The results are much clearer to comprehend:

o   The tree in the left pane of the window will just show the names of the function (callers or callees) recursively.

o   When you click on a result in the left pane, the actual call site appears in the right pane. 

o   Upon double clicking the call-site in the right-pane, you are taken to that location in your source code in the Editor. 

-          The user can limit the search results to the current file, the current project or the entire solution for better filtering and performance.

-          The user can copy the results from the call-sites pane and paste them elsewhere to perform some analysis.

There was no way to do any of the above using the old Call Browser.

The Call Hierarchy window can be invoked from the Editor context menu by just right-clicking on the function for which you want to see the results:

Call Hierarchy Invocation

 

To build this feature, we have made use of the new C++ IDE infrastructure, the details of which were posted here recently.  To populate the “calls from” information for a function say func, we simply need to parse the contents of func and find out all the functions being called from within it.  “Calls To” performs a wider search.  A query is made to the SQL based database (explained here again) to find out all the locations where func is referenced, and then for all the results, their enclosing blocks of code are parsed to find out whether this reference actually is a function call to func.  We have tried to make sure that the results we provide to you are accurate and complete.  However, we don’t claim to provide debugger-like accuracy.  For example, you will not see implicit calls like destructors etc. being listed in the list of calls from a function (some day maybeJ).  However, we have done enough to ensure that all the information relevant while designing, writing and reading code is available.  

Overall, we believe that new intuitive user interface, greater flexibility, fewer navigation steps, and better accuracy make the new Call Hierarchy a very useful feature.  We are excited about it and hope you will like it.

Thank you.

  • Please, code snippets for VC++ (like C#, VB)

  • Nice improvements, I would like to copy and paste also from the left pane (the tree). Will it be possible?

  • Thanks. Sounds very nice.

    However, the primary reason I don't use the existing call browser in 2005/8 is because its too slow in practice.

    In comparison I can trigger a "find in files.." search in a few key presses that effectively gives me the same information but much faster, albeit in an unorganised list.

    Incidentally, the same performance issue applies to other features such as a the class browser.

    Is the team addressing the performance issues in VS?

  • May I suggest using different icons for the "Call from" and "Call to" nodes in your tree, just to make them stand out better?

    Although, like Sam, I never use it the existing feature. I mostly need to know who is calling a functions, and for that I just do a quick "Find in files..." on my large C++ project.

  • Is there any chance that the underlying data can be exposed to enable something like "Give me a list of functions that aren't called from anywhere?"

  • Thanks for your comments.

    @fabio: copy/paste of the tree from left pane right now is not possible.  Individual nodes however can be copied and pasted.  I can see the value in copying the tree and then pasting it in a text-tree format.  We can certainly consider it for future.

    @Sam:

    @Joe: "Find in Files" is a grep of the text in files and will give you a lot of extraneous results (i.e. things that are not function calls or things that are not exactly what you specified).  Results from Call Hierarchy are effectively verified with the compiler for accuracy.  For example, if you do a "Calls To" for Start in MyClass::Start, it will never return result for YourClass::Start.  Doing this will off course require extra computation and hence the slowdown.  But, rest assured we have done work in this release to improve the performance and you will see the results soon.  And yes, we do have plans for different icons for "Calls To" and "Calls From"

    @CMC: Very interesting.  If this type of a problem is to be solved in the design-time space (IDE, IntelliSense, Navigation, Browsing etc.), I can see it becoming a huge performance issue.  Imagine what you need to do: Identify all functions inside your code, perform a "Calls To" for all the functions, and then list those for which "Calls To" returns zero results.  That's why, I guess this kind of information is better found through analysis of the binaries etc.

  • I would like a graphical view that shows each function as a node with calls being edges on a graph. It would be cool to see the whole graph at once to visualize the program's call structure.

  • I second that suggestion, a graphical view would indeed be useful.

  • A few things that would be nice would be the ability to regain control of the UI when doing remote debugging, or have it allow you to use the editor when the remote debugger disconnects (like if you are debugging in windbg on the kernel and are reviewing the code in VS while debugging the usermode code as well). Or if a network issue comes up that VS doesnt just lock up, or even lag on a remote network issue, that you can still do something.

    Also, is there a reason it has to chew up > 128Megs just to be a intelligent text editor? or even runaway up to a gig+ after extended usage (>6-8 hours of dev time?). Or perhaps when debugging GDI+ objects that it can really eat up memory?

    How about the ability to move code from VS 2005 to VS 2008 and still have the same STL functions work? or even have the SHBrowseForFolder function work when called twice (works with VS2005 compiled code, but in VS2008 random crashes, and just nutty). It completely destabilized the code that was rock solid, and all that was done was move from 2005 to 2008. When it crashed, it was always inside of either the STL call function, or an internal call function (6-7 calls deep outside of our code). Obviously we had to go back to 2005 where we are now and its completely stable, but because the random crashes just made it unusable in 2008.

    Oh, and using STL file calls are still limited to 2GB (size_t/signed integer), so doing file IO using fstream on larger objects isnt realistic unless you want to redefine that. Minor item, but still an annoyance, 2GB+ files are quite realistic these days.

    How about an option to allow the toolbar stay collapsed when moused over (as autohide is very nice, but is a nuisance when you hit that right side line of code and mouse over accidentially). Yes, could easily disble autohide, but having the toolbar on that side is actually quite useful for DB connects as well as the tool objects themselves.

    The #1 reason I use VS as my preferred editor is because I can get to things fast, look up objects and cross reference quickly. But the newer versions scare me quite a bit because moving up to new platforms seems to have quite a bit of pain, especially when using the same documented calls. Back in VC6 -> VS.net it was understandable, but now?

  • @Royal,

    what is your SHBrowseForFolder code? This is a windows function, so the VS version cannot make the difference here. There must be some fault in how you call or use it.

  • @Raman Sharma

    Thanks very much for your feedback.

    I understand a find in files isn't accurate. However it is accurate enough in practice. It actually serves to underline my point: Ultimately this tool is meant to speed up my development. A search, even with false positives is faster - and therefore more suitable - than an accurate tool that is slow. Performance really is critical.

    I think CMC makes a good point. This is functionality I would be interested in. Incidentally, it's also something I can get a draft version from with some crude parsing (unreferenced functions typically only have two entries - the .h and .cpp). The implementation you propose would indeed be very slow. Are there not faster implementations that would produce useful data?

    Thanks,

    Sam

  • Who cares about new features when you folks can't even get intellisense working? What about refactoring & syntax formatting?

  • Pretty simple clean cut code, and in 2005 works great. in 2008 unpredictable crashing.

    BROWSEINFO m_brinfo; (in the .h of CFolderDialog as its a skinned holder)

    CFolderDialog::CFolderDialog(CWnd* pWnd)

    {

    bCoInitSucceeded = (CoInitialize(NULL) == S_OK);

    }

    CFolderDialog::~CFolderDialog()

    {

    if (bCoInitSucceeded) { CoUninitialize (); }

    }

    LRESULT CStatusMsgDlg::PopPathDialog(WPARAM wParam, LPARAM lParam)

    {

    CFolderDialog dlg(this);

    PWCHAR pwcPath = (PWCHAR) wParam;

    (void) dlg.BrowseForFolder(L"Please Select the installation Path", BIF_NEWDIALOGSTYLE | BIF_VALIDATE, pwcPath, TRUE);

    SetEvent((HANDLE) lParam); // set the event to allow the app to continue

    return 0;

    }

    LPCITEMIDLIST CFolderDialog::BrowseForFolder(LPCTSTR title, UINT flags, LPWSTR csRoot, BOOL /*bFilter*/)

    {

    m_brinfo.lpszTitle = title;

    m_brinfo.ulFlags = flags;

    m_brinfo.lpfn = CallbackProc;

    m_brinfo.lParam = (LPARAM)this;

    m_sSelFolder = csRoot;

    LPCITEMIDLIST pidl = SHBrowseForFolder(&m_brinfo); // do it (Crash happens in this call 2008)

    return pidl;

    }

    int CALLBACK CFolderDialog::CallbackProc(HWND hwnd, UINT msg, LPARAM lp, LPARAM lpData)

    {

    CFolderDialog* pDlg = (CFolderDialog*)lpData;

    ASSERT(pDlg);

    if (pDlg != NULL) {

    if (pDlg->m_hWnd!=hwnd) { pDlg->m_hWnd = hwnd; };

    return pDlg->OnMessage(msg, lp);

    }

    return 0;

    }

    int CFolderDialog::OnMessage(UINT msg, LPARAM lp)

    {

    switch (msg) {

    case BFFM_INITIALIZED:

    OnInitialized();

    return 0;

    case BFFM_SELCHANGED:

    OnSelChanged((LPCITEMIDLIST)lp);

    return 0;

    case BFFM_VALIDATEFAILED:

    return OnValidateFailed((LPCTSTR)lp);

    default:

    TRACE(L"***Warning: unknown message %d in CFolderDialog::OnMessage\n");

    }

    return 0;

    }

  • "intellisense" -- what would be great is at least the ability to purge and reset intellisense so that it can work again when it dies instead of having to get out, delete the ncb and get back in.

    I find it dies on me quite a bit, and the deeper my code subclasses and inherits the worse it gets.

  • @Royal,

    read about CoInitialize here: you need to call CoUninitialize for CoInit == S_FALSE as well. So bCoInitSucceeded = SUCCEEDED(CoInitialize(NULL)) would be better. But: when you read the remarks section I would count it "dangerous" to call it in the middle of a program as you cannot make sure that if your thread is the first CoInit caller that it will be the last CoUninit caller as well - in fact you call CoUninit as soon as you destructor runs. If you control the exe as well you should CoInit somewhere early during app startup and CoUninit before it ends. When using COM in your own threads call CoInit at the beginning of your thread proc and CoUninit at its end - be sure to have Released all COM pointers used in that thread before calling CoUninit.

    You do not free the returned PIDL which you should with CoTaskMemFree if != NULL.

    You might likely have a multithread issue. I guess you do some multithread stuff due to your call to SetEvent.

    So I guess there are still some places where crashes could be caused by your code rather than by VC9. Just because your code runs compiled with VC8 does not say that it is correct.

Page 1 of 3 (35 items) 123