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