1 /*
2  * This file is part of the Code::Blocks IDE and licensed under the GNU General Public License, version 3
3  * http://www.gnu.org/licenses/gpl-3.0.html
4  *
5  * $Revision: 10549 $
6  * $Id: cbprofilerexec.cpp 10549 2015-10-31 11:21:01Z fuscated $
7  * $HeadURL: svn://svn.code.sf.net/p/codeblocks/code/branches/release-20.xx/src/plugins/contrib/profiler/cbprofilerexec.cpp $
8  */
9 
10 #include "sdk.h"
11 #ifndef CB_PRECOMP
12     #include <wx/event.h>
13     #include <wx/font.h>
14     #include <wx/intl.h>
15     #include <wx/listctrl.h>
16     #include <wx/notebook.h>
17     #include <wx/textctrl.h>
18     #include <wx/utils.h>
19     #include <wx/xrc/xmlres.h>
20     #include "globals.h"
21     #include "manager.h"
22     #include "logmanager.h"
23 #endif
24 #include <wx/busyinfo.h>
25 #include <wx/colour.h>
26 #include <wx/ffile.h>
27 #include <wx/filedlg.h>
28 #include <wx/progdlg.h>
29 #include "cbprofilerexec.h"
30 
31 BEGIN_EVENT_TABLE(CBProfilerExecDlg, wxScrollingDialog)
32     EVT_LIST_ITEM_ACTIVATED(XRCID("lstFlatProfile"), CBProfilerExecDlg::FindInCallGraph)
33     EVT_LIST_ITEM_ACTIVATED(XRCID("lstCallGraph"),   CBProfilerExecDlg::JumpInCallGraph)
34     EVT_BUTTON             (XRCID("btnExport"),      CBProfilerExecDlg::WriteToFile)
35     EVT_LIST_COL_CLICK     (XRCID("lstFlatProfile"), CBProfilerExecDlg::OnColumnClick)
36 END_EVENT_TABLE()
37 
38 // Static data for column sorting management
39 bool CBProfilerExecDlg::sortAscending = false;
40 int  CBProfilerExecDlg::sortColumn    = -1;
41 
Execute(wxString exename,wxString dataname,struct_config config)42 int CBProfilerExecDlg::Execute(wxString exename, wxString dataname, struct_config config)
43 {
44     // gprof optional parameters
45     wxString param = config.txtExtra;
46     if (config.chkAnnSource && !config.txtAnnSource.IsEmpty()) param << _T(" -A")  << config.txtAnnSource;
47     if (config.chkMinCount)                                    param << _T(" -m ") << config.spnMinCount;
48     if (config.chkBrief)                                       param << _T(" -b");
49     if (config.chkFileInfo)                                    param << _T(" -i");
50     if (config.chkUnusedFunctions)                             param << _T(" -z");
51     if (config.chkStaticCallGraph)                             param << _T(" -c");
52     if (config.chkNoStatic)                                    param << _T(" -a");
53     if (config.chkSum)                                         param << _T(" -s");
54 
55     wxString cmd;
56     cmd << _T("gprof ") << param << _T(" \"") << exename << _T("\" \"") << dataname << _T("\"");
57 
58     int pid = -1;
59 
60     { // begin lifetime of wxBusyInfo
61       wxBusyInfo wait(_("Please wait, while running gprof..."), parent);
62       Manager::Get()->GetLogManager()->DebugLog(F(_T("Profiler: Running command %s"), cmd.wx_str()));
63       pid = wxExecute(cmd, gprof_output, gprof_errors);
64     } // end lifetime of wxBusyInfo
65 
66     if (pid == -1)
67     {
68         wxString msg = _("Unable to execute gprof.\nBe sure the gprof executable is in the OS global path.\nC::B Profiler could not complete the operation.");
69         cbMessageBox(msg, _("Error"), wxICON_ERROR | wxOK, (wxWindow*)Manager::Get()->GetAppWindow());
70         Manager::Get()->GetLogManager()->DebugLog(msg);
71 
72         return -1;
73     }
74     else
75     {
76         wxXmlResource::Get()->LoadObject(this, parent, _T("dlgCBProfilerExec"),_T("wxScrollingDialog"));
77         wxFont font(10, wxFONTFAMILY_MODERN, wxFONTSTYLE_NORMAL, wxFONTWEIGHT_NORMAL);
78         outputFlatProfileArea     = XRCCTRL(*this, "lstFlatProfile",     wxListCtrl);
79         outputHelpFlatProfileArea = XRCCTRL(*this, "txtHelpFlatProfile", wxTextCtrl);
80         outputHelpFlatProfileArea->SetFont(font);
81         outputCallGraphArea       = XRCCTRL(*this, "lstCallGraph",       wxListCtrl);
82         outputHelpCallGraphArea   = XRCCTRL(*this, "txtHelpCallGraph",   wxTextCtrl);
83         outputHelpCallGraphArea->SetFont(font);
84         outputMiscArea            = XRCCTRL(*this, "txtMisc",            wxTextCtrl);
85         outputMiscArea->SetFont(font);
86 
87         if(!gprof_output.IsEmpty())
88             ShowOutput(gprof_output, false);
89         else
90             ShowOutput(gprof_errors, true);
91     }
92 
93     return 0;
94 }
95 
ShowOutput(const wxArrayString & msg,bool error)96 void CBProfilerExecDlg::ShowOutput(const wxArrayString& msg, bool error)
97 {
98     const size_t maxcount(msg.GetCount());
99     if ( !maxcount )
100         return;
101 
102     if (!error)
103     {
104         wxProgressDialog progress(_("C::B Profiler plugin"),_("Parsing profile information. Please wait..."), maxcount, NULL, wxPD_AUTO_HIDE|wxPD_APP_MODAL|wxPD_SMOOTH);
105 
106         // Parsing Flat Profile
107         size_t count(0);
108         if (msg[count].Find(_T("Flat profile")) != -1)
109             ParseFlatProfile(msg, progress, maxcount, count);
110 
111         // Parsing Call Graph
112         if ((count < maxcount) &&
113             (msg[count].Find(_T("Call graph"))   != -1))
114             ParseCallGraph(msg, progress, maxcount, count);
115 
116         // The rest of the lines, if any, is printed in the Misc tab
117         ParseMisc(msg, progress, maxcount, count);
118     }
119     else
120     {
121         wxString output;
122         for (size_t count(0); count < maxcount; ++count )
123         {
124             output << msg[count] << _T("\n");
125         }
126         outputMiscArea->SetValue(output);
127         const wxColour colour(255,0,0);
128         outputMiscArea->SetForegroundColour(colour);
129     }
130 
131     ShowModal();
132 }
133 
~CBProfilerExecDlg()134 CBProfilerExecDlg::~CBProfilerExecDlg()
135 {
136 }
137 
EndModal(int retCode)138 void CBProfilerExecDlg::EndModal(int retCode)
139 {
140     wxScrollingDialog::EndModal(retCode);
141 }
142 
143 // Sorting function of the flat profile columns
SortFunction(wxIntPtr item1,wxIntPtr item2,wxIntPtr sortData)144 int wxCALLBACK SortFunction(wxIntPtr item1, wxIntPtr item2, wxIntPtr sortData)
145 {
146     CBProfilerExecDlg *dialog = (CBProfilerExecDlg*) sortData;
147 
148     wxListCtrl *listCtrl = dialog->GetoutputFlatProfileArea();
149     int col = dialog->GetsortColumn();
150     long itemId1 = listCtrl->FindItem(-1, item1);
151     long itemId2 = listCtrl->FindItem(-1, item2);
152 
153     wxListItem listItem1, listItem2;
154 
155     listItem1.SetId(itemId1);
156     listItem1.SetColumn(col);
157     listItem1.SetMask(wxLIST_MASK_TEXT);
158     listCtrl->GetItem(listItem1);
159 
160     listItem2.SetId(itemId2);
161     listItem2.SetColumn(col);
162     listItem2.SetMask(wxLIST_MASK_TEXT);
163     listCtrl->GetItem(listItem2);
164 
165     // All the columns are composed with numbers except the last one
166     if (col == 6)
167     {
168        if (dialog->GetsortAscending())
169            return wxStrcmp(listItem1.GetText(), listItem2.GetText());
170        else
171            return wxStrcmp(listItem2.GetText(), listItem1.GetText());
172     }
173     else
174     {
175         double num1, num2;
176         double success = listItem1.GetText().ToDouble(&num1);
177         if (!success)
178         {
179             if (dialog->GetsortAscending()) return -1;
180             else                            return 1;
181         }
182         success = listItem2.GetText().ToDouble(&num2);
183         if (!success)
184         {
185             if (dialog->GetsortAscending()) return 1;
186             else                            return -1;
187         }
188         if (dialog->GetsortAscending())
189         {
190             if (num1 < num2)      return -1;
191             else if (num1 > num2) return 1;
192             else                  return 0;
193         }
194         else
195         {
196             if (num1 > num2)      return -1;
197             else if (num1 < num2) return 1;
198             else                  return 0;
199         }
200     }
201 }
202 
203 // Function called when a column header is clicked
OnColumnClick(wxListEvent & event)204 void CBProfilerExecDlg::OnColumnClick(wxListEvent& event)
205 {
206     if (event.GetColumn() != sortColumn)
207         sortAscending = true;
208     else
209         sortAscending = !sortAscending;
210 
211     sortColumn = event.GetColumn();
212     outputFlatProfileArea->SortItems(SortFunction, (wxIntPtr)this);
213 }
214 
ParseMisc(const wxArrayString & msg,wxProgressDialog & progress,const size_t maxcount,size_t & count)215 void CBProfilerExecDlg::ParseMisc(const wxArrayString& msg, wxProgressDialog &progress, const size_t maxcount, size_t &count)
216 {
217     // parsing
218     wxString output;
219     progress.Update(count, _("Parsing miscellaneous information. Please wait..."));
220     for ( ; count < maxcount; ++count )
221     {
222         if ((count%10) == 0) progress.Update(count);
223         output << msg[count] << _T("\n");
224     }
225     outputMiscArea->SetValue(output);
226 }
227 
ParseCallGraph(const wxArrayString & msg,wxProgressDialog & progress,const size_t maxcount,size_t & count)228 void CBProfilerExecDlg::ParseCallGraph(const wxArrayString& msg, wxProgressDialog &progress, const size_t maxcount, size_t& count)
229 {
230     // Setting colums names
231     outputCallGraphArea->InsertColumn(0, _T("index"),    wxLIST_FORMAT_CENTRE);
232     outputCallGraphArea->InsertColumn(1, _T("% time"),   wxLIST_FORMAT_CENTRE);
233     outputCallGraphArea->InsertColumn(2, _T("self"),     wxLIST_FORMAT_CENTRE);
234     outputCallGraphArea->InsertColumn(3, _T("children"), wxLIST_FORMAT_CENTRE);
235     outputCallGraphArea->InsertColumn(4, _T("called"),   wxLIST_FORMAT_CENTRE);
236     outputCallGraphArea->InsertColumn(5, _T("name"));
237 
238     // Jump header lines
239     progress.Update(count,_("Parsing call graph information. Please wait..."));
240     while ( (count < maxcount) && (msg[count].Find(_T("index % time")) == -1) )
241     {
242         if ((count%10) == 0) progress.Update(count);
243         ++count;
244     }
245     ++count;
246 
247     // Parsing Call Graph
248     size_t next(0);
249     wxListItem item;
250     wxString TOKEN;
251 
252     // setting listctrl default text colour for secondary lines
253     const wxColour COLOUR(wxTheColourDatabase->Find(_T("GREY")));
254 
255     for ( ; count < maxcount; ++count )
256     {
257         if ((count%10) == 0) progress.Update(count);
258 
259         TOKEN = msg[count];
260         if ( (TOKEN.IsEmpty()) || (TOKEN.Find(wxChar(0x0C)) != -1) )
261             break;
262 
263         outputCallGraphArea->InsertItem(count,_T(""));
264         char first_char = TOKEN.GetChar(0);
265         // treating the empty separator lines
266         if (first_char == '-')
267         {
268             outputCallGraphArea->SetItem(next, 0, _T(""));
269             item.Clear();
270             item.SetId(next);
271             item.SetBackgroundColour(*wxLIGHT_GREY);
272             outputCallGraphArea->SetItem(item);
273         }
274         else
275         {
276             outputCallGraphArea->SetItem(next, 0, TOKEN(0,6).Trim(true).Trim(false));
277             outputCallGraphArea->SetItem(next, 1, TOKEN(6,6).Trim(true).Trim(false));
278             outputCallGraphArea->SetItem(next, 2, TOKEN(12,8).Trim(true).Trim(false));
279             outputCallGraphArea->SetItem(next, 3, TOKEN(20,8).Trim(true).Trim(false));
280             outputCallGraphArea->SetItem(next, 4, TOKEN(28,17).Trim(true).Trim(false));
281             outputCallGraphArea->SetItem(next, 5, TOKEN.Mid(45));
282             if (first_char != '[')
283             {
284                 outputCallGraphArea->SetItemTextColour(next,COLOUR);
285             }
286         }
287         ++next;
288     }
289 
290     // Resize columns
291     outputCallGraphArea->SetColumnWidth(0, wxLIST_AUTOSIZE_USEHEADER );
292     outputCallGraphArea->SetColumnWidth(1, wxLIST_AUTOSIZE_USEHEADER );
293     outputCallGraphArea->SetColumnWidth(2, wxLIST_AUTOSIZE_USEHEADER );
294     outputCallGraphArea->SetColumnWidth(3, wxLIST_AUTOSIZE_USEHEADER );
295     outputCallGraphArea->SetColumnWidth(4, wxLIST_AUTOSIZE );
296     outputCallGraphArea->SetColumnWidth(5, wxLIST_AUTOSIZE);
297 
298     // Printing Call Graph Help
299     wxString output_help;
300     for ( ; count < maxcount; ++count )
301     {
302         if ((count%10) == 0) progress.Update(count);
303 
304         TOKEN = msg[count];
305         if (TOKEN.Find(wxChar(0x0C)) != -1)
306             break;
307 
308         output_help << TOKEN << _T("\n");
309     }
310     outputHelpCallGraphArea->SetValue(output_help);
311 
312     ++count;
313 }
314 
ParseFlatProfile(const wxArrayString & msg,wxProgressDialog & progress,const size_t maxcount,size_t & count)315 void CBProfilerExecDlg::ParseFlatProfile(const wxArrayString& msg, wxProgressDialog &progress, const size_t maxcount, size_t &count)
316 {
317     // Setting colums names
318     outputFlatProfileArea->InsertColumn(0, _T("% time"),        wxLIST_FORMAT_CENTRE);
319     outputFlatProfileArea->InsertColumn(1, _T("cum. sec."),     wxLIST_FORMAT_CENTRE);
320     outputFlatProfileArea->InsertColumn(2, _T("self sec."),     wxLIST_FORMAT_CENTRE);
321     outputFlatProfileArea->InsertColumn(3, _T("calls"),         wxLIST_FORMAT_CENTRE);
322     outputFlatProfileArea->InsertColumn(4, _T("self ms/call"),  wxLIST_FORMAT_CENTRE);
323     outputFlatProfileArea->InsertColumn(5, _T("total ms/call"), wxLIST_FORMAT_CENTRE);
324     outputFlatProfileArea->InsertColumn(6, _T("name"));
325 
326     // Jump header lines
327     progress.Update(count,_("Parsing flat profile information. Please wait..."));
328     while ((count < maxcount)&&(msg[count].Find(_T("time   seconds")) == -1))
329     {
330         ++count;
331     }
332     ++count;
333 
334     // Parsing Call Graph
335     size_t next(0);
336     unsigned int spacePos[6] = {6, 16, 25, 34, 43, 52};
337     wxString TOKEN;
338     for ( ; count < maxcount; ++count )
339     {
340         if ((count%10) == 0) progress.Update(count);
341 
342         TOKEN = msg[count];
343         if ( (TOKEN.IsEmpty()) || (TOKEN.Find(wxChar(0x0C)) != -1) )
344             break;
345 
346         long item = outputFlatProfileArea->InsertItem(next,_T(""));
347         outputFlatProfileArea->SetItemData(item, next);
348         // check that we have spaces where spaces are supposed to be
349         if (TOKEN.Len() > spacePos[5]) {
350             bool need_parsing = false;
351             for (int i=0; i<6; ++i)
352             {
353                 if (TOKEN[spacePos[i]] != ' ')
354                 {
355                     need_parsing = true;
356                     break;
357                 }
358             }
359             // if profile output is not in perfect table format
360             // manually parse for space positions
361             if (need_parsing)
362             {
363                 int cnt=0; int i=0; int len = TOKEN.Len();
364                 while (i < len && cnt < 6) {
365                     // we start with spaces
366                     while (TOKEN[i] == ' ' && ++i < len);
367                     if (i>=len) break;
368                     // now we parse everything else than
369                     while (TOKEN[i] != ' ' && ++i < len);
370                     if (i>=len) break;
371                     // found a new space position
372                     spacePos[cnt++] = i;
373                 }
374             }
375         }
376 
377         outputFlatProfileArea->SetItem(next, 0, ((TOKEN.Mid(0,spacePos[0])).Trim(true)).Trim(false));
378         for (int i(1); i<6; ++i)
379             outputFlatProfileArea->SetItem(next, i,((TOKEN.Mid(spacePos[i-1],spacePos[i] - spacePos[i-1])).Trim(true)).Trim(false));
380         outputFlatProfileArea->SetItem(next, 6, ((TOKEN.Mid(spacePos[5])).Trim(true)).Trim(false));
381 
382 /*
383         outputFlatProfileArea->SetItem(next, 0, ((TOKEN.Mid(0,6)).Trim(true)).Trim(false));
384         outputFlatProfileArea->SetItem(next, 1, ((TOKEN.Mid(6,10)).Trim(true)).Trim(false));
385         outputFlatProfileArea->SetItem(next, 2, ((TOKEN.Mid(16,9)).Trim(true)).Trim(false));
386         outputFlatProfileArea->SetItem(next, 3, ((TOKEN.Mid(25,9)).Trim(true)).Trim(false));
387         outputFlatProfileArea->SetItem(next, 4, ((TOKEN.Mid(34,9)).Trim(true)).Trim(false));
388         outputFlatProfileArea->SetItem(next, 5, ((TOKEN.Mid(43,9)).Trim(true)).Trim(false));
389         outputFlatProfileArea->SetItem(next, 6, ((TOKEN.Mid(52)).Trim(true)).Trim(false));
390 */
391         ++next;
392     }
393 
394     // Resize columns
395     outputFlatProfileArea->SetColumnWidth(0, wxLIST_AUTOSIZE_USEHEADER );
396     outputFlatProfileArea->SetColumnWidth(1, wxLIST_AUTOSIZE_USEHEADER );
397     outputFlatProfileArea->SetColumnWidth(2, wxLIST_AUTOSIZE_USEHEADER );
398     outputFlatProfileArea->SetColumnWidth(3, wxLIST_AUTOSIZE );
399     outputFlatProfileArea->SetColumnWidth(4, wxLIST_AUTOSIZE_USEHEADER );
400     outputFlatProfileArea->SetColumnWidth(5, wxLIST_AUTOSIZE_USEHEADER );
401     outputFlatProfileArea->SetColumnWidth(6, wxLIST_AUTOSIZE);
402 
403     // Printing Flat Profile Help
404     wxString output_help;
405     for ( ; count < maxcount; ++count )
406     {
407         if ((count%10) == 0) progress.Update(count);
408 
409         TOKEN = msg[count];
410         if (TOKEN.Find(wxChar(0x0C)) != -1)
411             break;
412 
413         output_help << msg[count] << _T("\n");
414     }
415     outputHelpFlatProfileArea->SetValue(output_help);
416 
417     ++count;
418 }
419 
420 // This function writes the gprof output to a file
WriteToFile(wxCommandEvent &)421 void CBProfilerExecDlg::WriteToFile(wxCommandEvent& /*event*/)
422 {
423     wxFileDialog filedialog(parent,
424                             _("Save gprof output to file"),
425                             wxEmptyString,
426                             wxEmptyString,
427                             _T("*.*"),
428                             wxFD_SAVE);
429 
430     if (filedialog.ShowModal() == wxID_OK)
431     {
432         wxFFile file(filedialog.GetPath().c_str(), _T("w"));
433         for (size_t n=0; n<gprof_output.GetCount(); ++n)
434         {
435             file.Write(gprof_output[n]);
436             file.Write(_T("\n"));
437         }
438         file.Close();
439     }
440 }
441 
442 // This function retrieves the selected function in the call graph tab
FindInCallGraph(wxListEvent & event)443 void CBProfilerExecDlg::FindInCallGraph(wxListEvent& event)
444 {
445     // We retrieve the name of the function on the line selected
446     wxListItem item;
447     item.SetId(event.GetIndex());
448     item.SetColumn(6);
449     item.SetMask(wxLIST_MASK_TEXT);
450     outputFlatProfileArea->GetItem(item);
451     const wxString function_name(item.GetText());
452 
453     // Then search this name in the call graph
454     wxString indexColumn;
455     int n;
456     for (n=0; n<outputCallGraphArea->GetItemCount(); ++n)
457     {
458         item.Clear();
459         item.SetId(n);
460         item.SetColumn(0);
461         item.SetMask(wxLIST_MASK_TEXT);
462         outputCallGraphArea->GetItem(item);
463         indexColumn = item.GetText();
464         if ((indexColumn.Mid(0,1)).compare(_T("[")) == 0)
465         {
466             item.Clear();
467             item.SetId(n);
468             item.SetColumn(5);
469             item.SetMask(wxLIST_MASK_TEXT);
470             outputCallGraphArea->GetItem(item);
471             if (item.GetText().Find(function_name) != -1)
472                 break;
473         }
474     }
475 
476     // Scrolling to the desired line in the "Call Graph" tab
477     outputCallGraphArea->SetItemState(item,wxLIST_STATE_SELECTED,wxLIST_STATE_SELECTED);
478     outputCallGraphArea->EnsureVisible(n);
479     XRCCTRL(*this, "tabs", wxNotebook)->SetSelection(1);
480 }
481 
482 // This function jumps to the selected function in the call graph tab
JumpInCallGraph(wxListEvent & event)483 void CBProfilerExecDlg::JumpInCallGraph(wxListEvent& event)
484 {
485     // We retrieve the name of the function on the line selected
486     wxListItem item;
487     item.SetId(event.GetIndex());
488     item.SetColumn(5);
489     item.SetMask(wxLIST_MASK_TEXT);
490     outputCallGraphArea->GetItem(item);
491     const wxString function_name(item.GetText());
492 
493     // Then search this name in the call graph
494     wxString indexColumn;
495     int n;
496     const int maxcount(outputCallGraphArea->GetItemCount());
497     for (n=0; n<maxcount; ++n)
498     {
499         item.Clear();
500         item.SetId(n);
501         item.SetColumn(0);
502         item.SetMask(wxLIST_MASK_TEXT);
503         outputCallGraphArea->GetItem(item);
504         indexColumn = item.GetText();
505 
506         if ((indexColumn.Mid(0,1)).compare(_T("[")) == 0)
507         {
508             item.Clear();
509             item.SetId(n);
510             item.SetColumn(5);
511             item.SetMask(wxLIST_MASK_TEXT);
512             outputCallGraphArea->GetItem(item);
513 
514             if (function_name.Find(item.GetText()) != wxNOT_FOUND)
515                 break;
516         }
517     }
518 
519     // Scrolling to the desired line in the "Call Graph" tab
520     outputCallGraphArea->SetItemState(item,wxLIST_STATE_SELECTED,wxLIST_STATE_SELECTED);
521     outputCallGraphArea->EnsureVisible(n);
522 }
523