Added collection of waiting events.

В окне "Status server" при получении информации о процессах добавлен сбор событий ожидания.
Должно быть установлено расширение pg_wait_sampling.
И правильно настроены параметры. Для примера минимальный размер буфера:
при частоте опроса 1 сек (1000мс), количестве процессов 100 (num_p),
pg_wait_sampling.history_period=10
Значение pg_wait_sampling.history_size = 1000 /10 * 100 = 10000
для 3-х кратного запаса можно взять 30000.
Ожидание ClientRead немного изменено и означает, ожидание данных от клиента в НАЧАТОЙ ТРАНЗАКЦИИ.
События ожидания можно сохранить в текстовый файл.
В настройках pgadmin3opt.json можно выбрать цвета для отдельных событий или отключить сбор.
This commit is contained in:
lsv 2024-09-24 13:18:02 +05:00
parent 1e121c1fa0
commit 450c00ea90
8 changed files with 5281 additions and 712 deletions

1699
ctl/wxTopActivity.cpp Normal file

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

583
include/ctl/wxTopActivity.h Normal file
View file

@ -0,0 +1,583 @@
#pragma once
#include <wx/wx.h>
#include "utils/WaitSample.h"
#include "wx/popupwin.h"
#include "wx/dataview.h"
#include "wx/headerctrl.h"
#include <wx/tokenzr.h>
#include <vector>
//#include "utils/misc.h"
// ----------------------------------------------------------------------------
// MyCustomRendererText
// ----------------------------------------------------------------------------
extern std::vector<int> sort_vec_map(const std::vector<int>& src, int& sum);
#define sepListWait ";"
extern int s_pid_HIGHLIGH;
class MyCustomRendererGraph : public wxDataViewCustomRenderer
{
public:
// This renderer can be either activatable or editable, for demonstration
// purposes. In real programs, you should select whether the user should be
// able to activate or edit the cell and it doesn't make sense to switch
// between the two -- but this is just an example, so it doesn't stop us.
explicit MyCustomRendererGraph(wxDataViewCellMode mode, int column)
: wxDataViewCustomRenderer("string", mode, wxALIGN_LEFT)
{
EnableEllipsize(wxELLIPSIZE_END);
col = column;
}
virtual bool Render(wxRect rect, wxDC* dc, int state) wxOVERRIDE
{
wxString l;
wxRect orig = rect;
if (col < 2) {
l = m_value;
if (m_value == "ffffffffffffffff" || m_value == "-1") {
l = "SUM";
RenderText(l, 0, rect, dc, state);
return true;
}
}
if (col == 0) {
if (m_value[0] == '*') {
l = m_value.AfterFirst('*');
// wxFont fn = dc->GetFont().Italic();
// wxDCFontChanger nfont(*dc, fn);
dc->SetBrush(*wxLIGHT_GREY_BRUSH);
dc->SetPen(*wxTRANSPARENT_PEN);
rect.Deflate(1);
dc->DrawRoundedRectangle(rect, 3);
RenderText(l, 0, rect, dc, state);
}
else
{
l = m_value;
RenderText(l, 0, rect, dc, state);
}
return true;
}
if (col == 1) {
RenderText(l, 0, rect, dc, state);
return true;
}
//dc->SetBrush( *wxLIGHT_GREY_BRUSH );
dc->SetBrush(*wxYELLOW_BRUSH);
//dc->SetPen(*wxTRANSPARENT_PEN);
dc->SetPen(*wxBLACK_PEN);
rect.Deflate(1);
//WaitSample*ws= static_cast<topDataViewCtrl*>(GetView())->GetSample();
//dc->DrawRoundedRectangle( rect, 3 );
wxFont fn = dc->GetFont();
fn.SetPointSize(fn.GetPointSize() - 1);
wxDCFontChanger nfont(*dc, fn);
wxString s = m_value, t;
wxStringTokenizer tk(s, sepListWait, wxTOKEN_DEFAULT);
bool isDis = true;
bool isGrp = true;
int f = 0;
float itog = 0;
int widt = orig.width - 20;
wxPoint p(orig.x, orig.y);
while (tk.HasMoreTokens())
{
l = tk.GetNextToken();
if (f == 0) {
float tmp;
l = l.AfterLast(' ');
wxSscanf(l, "%f", &tmp);
itog = tmp;
float sek = itog / 1000;
wxString tt = wxString::Format("%.1f", sek); // time sec
wxSize sz = GetTextExtent(tt);
wxRect drw(orig.x + orig.width - sz.x - 2, orig.y, sz.x + 2, orig.GetHeight());
wxRect npos(0, 0, sz.x, sz.y);
npos = npos.CentreIn(drw);
dc->DrawText(tt, npos.x, npos.y);
widt = orig.width - sz.x - 2;
}
if (f > 0) {
float tmp;
wxString w;
long cl;
l = l.AfterFirst(' ');
wxSscanf(l, "%ld %f", &cl, &tmp);
wxColour c(cl);
//c.Set(cl);
dc->SetBrush(c);
int ww = widt * tmp;
wxRect drw(p.x, p.y, ww, rect.GetHeight());
float sek = itog * tmp / 1000;
wxString tt = wxString::Format("%.1f", sek); // time sec
wxSize sz = GetTextExtent(tt);
wxRect npos(0, 0, sz.x, sz.y);
npos = npos.CentreIn(drw);
dc->DrawRectangle(drw);
{
wxColour fg(ContrastColorBlackOrWhite(c));
wxDCTextColourChanger setfg(*dc, fg);
if (sz.GetWidth() < drw.GetWidth()) dc->DrawText(tt, npos.x, npos.y);
}
p.x = p.x + ww;
}
f++;
}
return true;
}
virtual bool ActivateCell(const wxRect& WXUNUSED(cell),
wxDataViewModel* WXUNUSED(model),
const wxDataViewItem& WXUNUSED(item),
unsigned int WXUNUSED(col),
const wxMouseEvent* mouseEvent) wxOVERRIDE
{
return false;
}
virtual wxSize GetSize() const wxOVERRIDE
{
wxSize txtSize = GetTextExtent(m_value);
int lines = m_value.Freq('\n') + 1;
if (lines > 1) {
// wxLogMessage("MyCustomRendererText GetSize() %s", position);
txtSize.SetHeight(txtSize.GetHeight() * lines + 1 * lines);
}
else {
#ifdef __WXGTK__
txtSize.SetHeight(txtSize.GetHeight() + 3);
#else
txtSize.SetHeight(-1);
#endif
}
if (col == 2) txtSize.SetWidth(-1);
else
{
//maxw = txtSize.GetWidth();
//maxw=wxMax(txtSize.GetWidth(), maxw);
txtSize.SetWidth(txtSize.GetWidth() + 1);
}
return txtSize;
//return GetView()->FromDIP(wxSize(60, 20));
}
virtual bool SetValue(const wxVariant& value) wxOVERRIDE
{
m_value = value.GetString();
return true;
}
virtual bool GetValue(wxVariant& WXUNUSED(value)) const wxOVERRIDE { return true; }
#if wxUSE_ACCESSIBILITY
virtual wxString GetAccessibleDescription() const wxOVERRIDE
{
return m_value;
}
#endif // wxUSE_ACCESSIBILITY
virtual bool HasEditorCtrl() const wxOVERRIDE { return false; }
private:
wxString m_value;
int col;
int maxw = -1;
};
class MyIndexListModel : public wxDataViewIndexListModel
{
public:
MyIndexListModel(WaitSample* w) { ws = w; }
void SetAllRows(const std::vector<key3>& keys, const std::map<key3, vec_int>& wait, bool isfp) {
k = keys;
w = wait;
//IsFirstPid = isfp;
Reset(keys.size());
}
key3 GetRowValue(long row) {
key3 r{};
if (row >= 0 && row < k.size()) {
r = k[row];
}
return r;
}
// Implement base class pure virtual methods.
unsigned GetCount() const wxOVERRIDE { return k.size(); }
void GetValueByRow(wxVariant& val, unsigned row, unsigned col) const wxOVERRIDE
{
//val=wxString::Format("r:%d,c:%d",row,col);
//val = m_strings[row];
//if (row + 1 < m_strings.Count() && col == 1 ) val = m_strings[row + 1];
bool ishint = false;
int r = row;
if (!val.IsNull()) {
//r = r * -1;
ishint = true;
}
if (k.size() > 0) {
key3 kk = k.at(r);
if ((col == 0)) {
long ttmp = kk.pid;
if (kk.sum > 0) val = wxString::Format("*%ld", ttmp); // backend
else
val = wxString::Format("%ld", ttmp);
}
else
val = wxString::Format("%llx", kk.qid);
if (col == 2) {
vec_int v = w.at(kk);
int sz = v.size();
val = wxString::Format("count w=%d", sz);
int itog = 0;
std::vector<int> map_sum_all = sort_vec_map(v, itog);
int period = ws->getPeriod();
int total = itog;
wxString h;
if (total != 0) {
wxString x = wxString::Format("all %ld", (long)itog * period);
for (int i = 0; i < map_sum_all.size(); i++) {
int wait_index = map_sum_all[i];
if (v[wait_index] == 0) continue;
float w_sum = (v[wait_index] / (float)total);
float sek = ((period * v[wait_index]) / 1000.0);
wxString w_name = ws->GetName(wait_index, WAIT_NAME);
wxString w_grp = ws->GetName(wait_index, WAIT_GRP);
wxString full = ws->GetName(wait_index, WAIT_FULL);
long clr = ws->GetColorByWaitName(full);
wxString pr = wxString::Format("%s %ld %.3f", w_name, clr, w_sum);
if (ishint) {
if (!h.IsEmpty()) h += "\n";
h.Append(wxString::Format("%-8.1f%-15s", sek, w_name));
}
else {
if (!x.IsEmpty()) x += sepListWait;
x += pr;
}
}
if (ishint) val = h;
else
val = x;
}
}
}
}
bool SetValueByRow(const wxVariant&, unsigned, unsigned) wxOVERRIDE
{
return false;
}
unsigned GetRow(const wxDataViewItem& item) const wxOVERRIDE { return (unsigned long)item.GetID()-1; } // 0 row = header
unsigned int GetColumnCount() const wxOVERRIDE { return 0; }
wxString GetColumnType(unsigned int) const wxOVERRIDE { return ""; }
private:
bool IsFirstPid = true;
std::vector<key3> k;
std::map<key3, vec_int> w;
WaitSample* ws = NULL;
wxDECLARE_NO_COPY_CLASS(MyIndexListModel);
};
//----------------------------------------------------------------------------
// SimpleTransientPopup
//----------------------------------------------------------------------------
class wxTopActivity;
class topDataViewCtrl;
class SimpleTransientPopup : public wxFrame
{
public:
friend class wxTopActivity;
friend class topDataViewCtrl;
SimpleTransientPopup(wxWindow* parent, bool scrolled, wxTopActivity* small_ctl, wxPoint p,wxString title);
virtual ~SimpleTransientPopup();
// wxPopupTransientWindow virtual methods are all overridden to log them
private:
wxScrolledWindow* m_panel;
wxTopActivity* top;
topDataViewCtrl* dvc;
wxObjectDataPtr<MyIndexListModel> m_index_list_model;
wxPoint mouseDownPos_;
bool dragg = false;
wxTimer m_taskTimer;
private:
void OnMouse(wxMouseEvent& event);
void OnSize(wxSizeEvent& event);
void OnSetFocus(wxFocusEvent& event);
void OnKillFocus(wxFocusEvent& event);
void OnTimerEvent(wxTimerEvent& pEvent);
void OnClose(wxCommandEvent& event);
private:
wxDECLARE_ABSTRACT_CLASS(SimpleTransientPopup);
wxDECLARE_EVENT_TABLE();
};
struct title_info {
bool iner = false;
bool remove;
bool down_line;
int total;
wxString title;
};
/// <summary>
/// Графический элемент отображающий данные WaitSample
/// Может использоваться в двух режимах.
/// </summary>
class wxTopActivity : public wxControl
{
WaitSample* ws;
wxString m_title;
wxSize setsize;
wxArrayString seriosName;
std::vector<wxDateTime> xAxis;
std::vector<wxTimeSpan> yAxis;
std::vector<vec_int> val;
std::vector<long> colors;
std::vector<title_info> m_title_i;
std::map<wxString, int> m_collapse;
std::map<wxString, int> m_sumtotal;
wxPoint mouse, mouseS;
wxRect m_area;
int m_click = 0;
int m_fix_detail_idx = -1;
// fix sel Range
wxDateTime fix_pos_L;
wxDateTime fix_pos_R;
wxPoint m_fix_pos; // context win
wxString m_filter_detail;
long long m_qid_filter = -1;
bool m_regroup = false;
int m_lastInterval = 0;
int m_RightTime = -1;
wxString m_filter;
int m_agg_int = 5000;
int m_count_wait;
int m_inter[9] = {
5000 ,
10000 ,
30000 ,//30 s
60000 ,//1 min
5 * 60000 ,//5 min
10 * 60000,//10 min
15 * 60000,//10 min
30 * 60000,//30 min
60 * 60000 //60 min
};
SimpleTransientPopup* m_simplePopup;
void paintSelRange(wxDC& dc, int width_sample);
public:
wxTopActivity(wxWindow* parent, WaitSample* WS, wxSize sz);
wxSize DoGetBestClientSize();
void setViewRange(int m_aggregate_interval, int RightTime);
WaitSample* getViewRange(int& m_aggregate_interval, int& RightTime);
void paintEvent(wxPaintEvent& evt);
void OnEraseBackground(wxEraseEvent& event);
void paintNow();
void SetFilter(long long qid);
/// <summary>
/// Возвращает время указаной границы
/// </summary>
/// <param name="isLeftRange">true для левой границы, инаце правая</param>
///
int getTimeSelRange(bool isLeftRange);
void render(wxDC& dc);
// some useful events
void mouseMoved(wxMouseEvent& event);
void mouseDown(wxMouseEvent& event);
void mouseWheelMoved(wxMouseEvent& event);
void mouseReleased(wxMouseEvent& event);
void rightClick(wxMouseEvent& event);
void mouseLeftWindow(wxMouseEvent& event);
void keyPressed(wxKeyEvent& event);
void keyReleased(wxKeyEvent& event);
void resize(wxSizeEvent& event);
DECLARE_EVENT_TABLE()
};
class topDataViewCtrl :
public wxDataViewCtrl
{
public:
topDataViewCtrl(wxWindow* parent, wxWindowID id,
const wxPoint& pos = wxDefaultPosition,
const wxSize& size = wxDefaultSize, long style = 0,
wxTopActivity* topactive = NULL,
const wxValidator& validator = wxDefaultValidator,
const wxString& name = wxASCII_STR(wxDataViewCtrlNameStr)
) : wxDataViewCtrl(parent, id, pos, size, style, validator, name)
{
//SetMinSize(wxSize(300,200));
top = topactive;
w = NULL;
w = top->getViewRange(agg, right_g);
Bind(wxEVT_DATAVIEW_COLUMN_HEADER_CLICK, [&](wxDataViewEvent& event) {
int col = event.GetColumn();
if (GetColumn(col)->GetTitle() == "PID") {
ignoreBG = !ignoreBG;
CalcRowsDataView(true, ispidfirst);
Refresh();
}
});
Bind(wxEVT_DATAVIEW_ITEM_ACTIVATED, [&](wxDataViewEvent& event) {
int col = event.GetColumn();
wxDataViewItem item(event.GetItem());
long row = (long)item.GetID();
row--;
if (col == 1) //qid
{
MyIndexListModel* m = static_cast<MyIndexListModel*>(GetModel());
key3 k = m->GetRowValue(row);
if (k.qid != -1) top->SetFilter(k.qid);
}
if (col == 0) { //pid
MyIndexListModel* m = static_cast<MyIndexListModel*>(GetModel());
key3 k = m->GetRowValue(row);
if (k.pid>0) s_pid_HIGHLIGH = k.pid;
// else
// s_pid_HIGHLIGH = -1;
}
});
Bind(wxEVT_DATAVIEW_COLUMN_REORDERED, [&](wxDataViewEvent& event) {
wxDataViewColumn* const col = event.GetDataViewColumn();
if (!col)
{
return;
}
ispidfirst = col->GetTitle() == "PID" && event.GetColumn() == 0;
if (!ispidfirst) ispidfirst = col->GetTitle() == "QID" && event.GetColumn() == 1;
CalcRowsDataView(true, ispidfirst);
Refresh();
});
GetMainWindow()->Bind(wxEVT_MOTION, [&](wxMouseEvent& event) {
if (event.Dragging())
return;
wxClientDC dc(GetMainWindow());
PrepareDC(dc);
//wxPoint logPos(event.GetLogicalPosition(dc));
//wxPoint mc= GetMainWindow()->ScreenToClient(event.GetPosition());
wxPoint mc = event.GetPosition();
wxString position;
//position = wxString::Format("x=%d y=%d", logPos.x, logPos.y);
//wxLogMessage("Mouse pos %s", position);
int dy = 0;
wxSize sz;
#ifdef WIN32
wxHeaderCtrl* const header = GenericGetHeader();
if (header) {
sz = header->GetSize();
//header->Refresh();
dy = sz.GetHeight();
}
mc.y += dy;
#else
mc.y += 18; // only linux compile
#endif
wxDataViewItem item;
wxDataViewColumn* column;
HitTest(mc, item, column);
if (item != NULL && column != NULL)
{
int row = (long)item.GetID() - 1;
int ncol = column->GetModelColumn();
if (row >= 0) {
if (column && column->GetModelColumn() == 2) {
wxVariant vr;
vr = "gethint";
if ((lastcol != ncol) || (lastrow != row)) {
MyIndexListModel* m = static_cast<MyIndexListModel*>(GetModel());
m->GetValueByRow(vr, row, ncol);
GetMainWindow()->SetToolTip(vr.GetString());
lastrow = row;
lastcol = ncol;
}
}
else if (column && column->GetModelColumn() == 0 ) {
// PID
MyIndexListModel* m = static_cast<MyIndexListModel*>(GetModel());
key3 k=m->GetRowValue(row);
if (k.sum > 0) {
if (((lastcol != ncol) || (lastrow != row))) {
wxString s = w->GetBackendTypeName(k.sum);
GetMainWindow()->SetToolTip(s);
lastrow = row;
lastcol = ncol;
}
}
else { GetMainWindow()->UnsetToolTip(); lastcol = -1;
}
}
else
{
GetMainWindow()->UnsetToolTip();
lastcol = -1;
}
}
}
else
{
GetMainWindow()->UnsetToolTip();
lastcol = -1;
}
});
}
WaitSample* GetSample() { return w; }
void BuildColumn(int mode);
bool CalcRowsDataView(bool force, bool IsPidFirst);
//void AddRow(wxString csvtext);
//void OnMouseMove(wxMouseEvent& event);
//void OnMouseDown(wxMouseEvent& event);
//void OnKEY_DOWN(wxKeyEvent& event);
//void OnKEY_UP(wxKeyEvent& event);
//void OnEVT_DATAVIEW_COLUMN_HEADER_CLICK(wxDataViewEvent& event);
//void OnEVT_DATAVIEW_CONTEXT_MENU(wxCommandEvent& event);
//void OnEVT_DATAVIEW_SELECTION_CHANGED(wxDataViewEvent& event);
//void OnContextMenu(wxDataViewEvent& event);
DECLARE_EVENT_TABLE()
private:
int lastrow = -1, lastcol = 1;
int agg, right_g, left_g;
bool ignoreBG = false;
bool ispidfirst = true;
wxTopActivity* top;
WaitSample* w;
};
#undef sepListWait

View file

@ -94,7 +94,7 @@ protected:
wxString dlgName;
wxString lastFilename, lastDir, lastPath;
wxString recentKey;
wxMenu *fileMenu, *editMenu, *viewMenu, *recentFileMenu, *helpMenu;
wxMenu *fileMenu, *editMenu, *viewMenu, *recentFileMenu, *helpMenu, *waitMenu;
wxStatusBar *statusBar;
wxMenuBar *menuBar;
ctlMenuToolbar *toolBar;

View file

@ -22,57 +22,63 @@
// wxAUI
#include <wx/aui/aui.h>
#include <wx/msgqueue.h>
#include "dlg/dlgClasses.h"
#include "utils/factory.h"
#include "ctl/ctlAuiNotebook.h"
#include "ctl/ctlNavigatePanel.h"
#include "utils/WaitSample.h"
#include "ctl/wxTopActivity.h"
enum
{
CTL_RATECBO = 250,
CTL_REFRESHBTN,
CTL_CANCELBTN,
CTL_TERMINATEBTN,
CTL_COMMITBTN,
CTL_ROLLBACKBTN,
CTL_LOGCBO,
CTL_ROTATEBTN,
CTL_STATUSLIST,
CTL_LOCKLIST,
CTL_XACTLIST,
CTL_LOGLIST,
CTL_QUERYSTATELIST,
MNU_STATUSPAGE,
MNU_LOCKPAGE,
MNU_XACTPAGE,
MNU_LOGPAGE,
MNU_QUERYSTATEPAGE,
MNU_TERMINATE,
MNU_COMMIT,
MNU_ROLLBACK,
MNU_COPY_QUERY,
MNU_CLEAR_FILTER_SERVER_STATUS,
MNU_COPY_QUERY_PLAN,
MNU_HIGHLIGHTSTATUS,
MNU_QUERYSTATEVERBOSE,
MNU_QUERYSTATETIME,
MNU_QUERYSTATEBUFFER,
MNU_QUERYSTATETRIGGER,
TIMER_REFRESHUI_ID,
TIMER_STATUS_ID,
TIMER_LOCKS_ID,
TIMER_XACT_ID,
TIMER_LOG_ID,
TIMER_QUERYSTATE_ID
CTL_RATECBO = 250,
CTL_REFRESHBTN,
CTL_CANCELBTN,
CTL_TERMINATEBTN,
CTL_COMMITBTN,
CTL_ROLLBACKBTN,
CTL_LOGCBO,
CTL_ROTATEBTN,
CTL_STATUSLIST,
CTL_LOCKLIST,
CTL_XACTLIST,
CTL_LOGLIST,
CTL_QUERYSTATELIST,
MNU_STATUSPAGE,
MNU_LOCKPAGE,
MNU_XACTPAGE,
MNU_LOGPAGE,
MNU_QUERYSTATEPAGE,
MNU_TERMINATE,
MNU_COMMIT,
MNU_ROLLBACK,
MNU_COPY_QUERY,
MNU_CLEAR_FILTER_SERVER_STATUS,
MNU_COPY_QUERY_PLAN,
MNU_HIGHLIGHTSTATUS,
MNU_QUERYSTATEVERBOSE,
MNU_QUERYSTATETIME,
MNU_QUERYSTATEBUFFER,
MNU_QUERYSTATETRIGGER,
MNU_WAITENABLE,
MNU_WAITSAVE,
TIMER_REFRESHUI_ID,
TIMER_STATUS_ID,
TIMER_LOCKS_ID,
TIMER_XACT_ID,
TIMER_LOG_ID,
TIMER_QUERYSTATE_ID
};
enum
{
PANE_STATUS = 1,
PANE_LOCKS,
PANE_XACT,
PANE_LOG,
PANE_QUERYSTATE
PANE_STATUS = 1,
PANE_LOCKS,
PANE_XACT,
PANE_LOG,
PANE_QUERYSTATE
};
@ -87,7 +93,7 @@ enum
#ifdef __WXGTK__
#define FRMSTATUS_DEFAULT_PERSPECTIVE wxT("layout2|name=Activity;caption=Activity;state=6309884;dir=4;layer=0;row=0;pos=0;prop=100000;bestw=20;besth=20;minw=-1;minh=-1;maxw=-1;maxh=-1;floatx=-1;floaty=-1;floatw=-1;floath=-1|name=Locks;caption=Locks;state=6293500;dir=4;layer=0;row=0;pos=1;prop=100000;bestw=20;besth=20;minw=-1;minh=-1;maxw=-1;maxh=-1;floatx=-1;floaty=-1;floatw=-1;floath=-1|name=Transactions;caption=Prepared Transactions;state=6293500;dir=4;layer=0;row=0;pos=2;prop=100000;bestw=20;besth=20;minw=-1;minh=-1;maxw=-1;maxh=-1;floatx=-1;floaty=-1;floatw=-1;floath=-1|name=Querystate;caption=QueryState;state=6293502;dir=5;layer=0;row=0;pos=0;prop=100000;bestw=20;besth=20;minw=-1;minh=-1;maxw=-1;maxh=-1;floatx=-1;floaty=-1;floatw=-1;floath=-1|name=Logfile;caption=Logfile;state=6293500;dir=5;layer=0;row=0;pos=0;prop=100000;bestw=20;besth=20;minw=-1;minh=-1;maxw=-1;maxh=-1;floatx=-1;floaty=-1;floatw=-1;floath=-1|name=toolBar;caption=Tool bar;state=2108144;dir=1;layer=10;row=0;pos=0;prop=100000;bestw=690;besth=39;minw=-1;minh=-1;maxw=-1;maxh=-1;floatx=-1;floaty=-1;floatw=-1;floath=-1|dock_size(4,0,0)=800|dock_size(5,0,0)=22|dock_size(1,10,0)=41|")
#else
#define FRMSTATUS_DEFAULT_PERSPECTIVE wxT("layout2|name=Activity;caption=Activity;state=6293500;dir=4;layer=0;row=1;pos=0;prop=100000;bestw=20;besth=20;minw=-1;minh=-1;maxw=-1;maxh=-1;floatx=174;floaty=216;floatw=578;floath=282|name=Locks;caption=Locks;state=6293500;dir=4;layer=0;row=1;pos=2;prop=100000;bestw=20;besth=20;minw=-1;minh=-1;maxw=-1;maxh=-1;floatx=136;floaty=339;floatw=576;floath=283|name=Transactions;caption=Transactions;state=6293500;dir=4;layer=0;row=1;pos=3;prop=100000;bestw=20;besth=20;minw=-1;minh=-1;maxw=-1;maxh=-1;floatx=133;floaty=645;floatw=577;floath=283|name=Querystate;caption=Query State;state=6309884;dir=4;layer=0;row=1;pos=1;prop=100000;bestw=20;besth=20;minw=-1;minh=-1;maxw=-1;maxh=-1;floatx=154;floaty=255;floatw=1360;floath=751|name=Logfile;caption=Logfile;state=6293500;dir=5;layer=0;row=0;pos=0;prop=100000;bestw=20;besth=20;minw=-1;minh=-1;maxw=-1;maxh=-1;floatx=-1;floaty=-1;floatw=-1;floath=-1|name=toolBar;caption=toolBar;state=2108144;dir=1;layer=10;row=0;pos=0;prop=100000;bestw=766;besth=39;minw=-1;minh=-1;maxw=-1;maxh=-1;floatx=586;floaty=525;floatw=483;floath=49|dock_size(1,10,0)=25|dock_size(4,0,1)=1115|dock_size(5,0,0)=22|")
#define FRMSTATUS_DEFAULT_PERSPECTIVE wxT("layout2|name=Activity;caption=Activity;state=6293500;dir=4;layer=0;row=1;pos=0;prop=100000;bestw=20;besth=20;minw=-1;minh=-1;maxw=-1;maxh=-1;floatx=174;floaty=216;floatw=578;floath=282|name=Locks;caption=Locks;state=6293500;dir=4;layer=0;row=1;pos=2;prop=100000;bestw=20;besth=20;minw=-1;minh=-1;maxw=-1;maxh=-1;floatx=136;floaty=339;floatw=576;floath=283|name=Transactions;caption=Transactions;state=6293500;dir=4;layer=0;row=1;pos=3;prop=100000;bestw=20;besth=20;minw=-1;minh=-1;maxw=-1;maxh=-1;floatx=133;floaty=645;floatw=577;floath=283|name=Querystate;caption=Query State;state=6309884;dir=4;layer=0;row=1;pos=4;prop=100000;bestw=20;besth=20;minw=-1;minh=-1;maxw=-1;maxh=-1;floatx=154;floaty=255;floatw=800;floath=751|name=Logfile;caption=Logfile;state=6293500;dir=4;layer=0;row=1;pos=5;prop=100000;bestw=20;besth=20;minw=-1;minh=-1;maxw=-1;maxh=-1;floatx=150;floaty=90;floatw=400;floath=800|name=toolBar;caption=toolBar;state=2108144;dir=1;layer=10;row=0;pos=0;prop=100000;bestw=766;besth=39;minw=-1;minh=-1;maxw=-1;maxh=-1;floatx=586;floaty=525;floatw=483;floath=49|dock_size(1,10,0)=25|dock_size(4,0,1)=1115|dock_size(5,0,0)=22|")
#endif
#endif
@ -96,175 +102,268 @@ static wxCriticalSection gs_critsect;
// Class declarations
static wxMutex s_mutexDBLogReading;
static wxSemaphore s_goReadLog(0, 1);
static wxSemaphore s_ResultOkLog(0, 1);
static wxSemaphore s_CloseLog(0, 1);
class ReadLogThread : public wxThread
{
public:
ReadLogThread(pgConn *conlog, wxWindow* p, wxMessageQueue<wxString> *queue)
{
log_queue = queue;
theParent = p;
logfileName = wxEmptyString;
savedPartialLine=wxEmptyString;
logfileLength=0;
len=0;
conn = conlog;
//nextrun = wxDateTime(0);
}
~ReadLogThread();
void DoTerminate();
bool IsTerminate() { return m_exit; }
void SetParameters(wxString plogfileName, long plenfile, long plogfileLength, wxString psavedPartialLine) {
logfileName = plogfileName;
len = plenfile;
logfileLength = plogfileLength;
if (plogfileLength==0) inquote = false;
savedPartialLine = psavedPartialLine;
}
long GetReadByteFile(wxString &part) {
part = savedPartialLine;
return logfileLength;
}
bool isReadyRows() {
wxMutexError e = s_mutexDBLogReading.TryLock();
if (e == wxMUTEX_NO_ERROR) {
s_mutexDBLogReading.Unlock();
return true;
}
return false;
}
void BreakRead();
bool isBreak() { return m_break; }
bool GoReadRows() {
m_break = false;
wxSemaError e = s_goReadLog.Post();
if (e == wxSEMA_NO_ERROR) {
return true;
}
//wxLogError("Semafore return error");
return false;
}
virtual void* Entry();
private:
void readLogFile(wxString logfileName, long& lenfile, long& logfileLength, wxString& savedPartialLine);
void getFilename();
void sendText(wxString s) {
wxThreadEvent e(wxEVT_THREAD);
e.SetString(s);
theParent->GetEventHandler()->AddPendingEvent(e);
}
wxDateTime nextrun;
wxWindow* theParent;
bool m_exit = false;
bool m_break = false;
pgConn *conn;
wxMessageQueue<wxString>* log_queue;
//
wxString logfileName;
wxString savedPartialLine;
long logfileLength;
long len;
bool inquote = false;
};
class frmStatus : public pgFrame
{
public:
frmStatus(frmMain *form, const wxString &_title, pgConn *conn);
~frmStatus();
void Go();
frmStatus(frmMain *form, const wxString &_title, pgConn *conn);
~frmStatus();
void Go();
private:
wxAuiManager manager;
wxAuiManager manager;
frmMain *mainForm;
pgConn *connection, *locks_connection;
frmMain *mainForm;
pgConn *connection, *locks_connection, *logconn;
wxString logFormat;
bool logHasTimestamp, logFormatKnown;
int logFmtPos;
wxString logFormat,logFindString;
bool logHasTimestamp, logFormatKnown;
int logFmtPos;
long logFinditem=-1;
ReadLogThread *logThread=NULL;
wxMessageQueue<wxString> log_queue;
wxDateTime logfileTimestamp, latestTimestamp;
wxString logDirectory, logfileName;
wxDateTime logfileTimestamp, latestTimestamp;
wxString logDirectory, logfileName;
wxString savedPartialLine;
wxString savedPartialLine;
bool showCurrent, isCurrent;
bool showCurrent, isCurrent;
long backend_pid;
int wait_event_type_col;
bool isrecovery,track_commit_timestamp, is_read_log;
bool wait_sample, wait_enable, wait_save;
bool frm_exit = false; // need close form
bool logisread = false; // need close form
WaitSample WS;
wxTopActivity* top_small;
wxString first_tt;
bool loaded;
long logfileLength;
wxColour logcol[2];
int addodd = 0;
int currentPane;
long backend_pid;
int wait_event_type_col;
bool isrecovery,track_commit_timestamp;
bool loaded;
long logfileLength;
wxColour bgColor;
int statusSortColumn;
wxString statusSortOrder;
int lockSortColumn;
wxString lockSortOrder;
int xactSortColumn;
wxString xactSortOrder;
int currentPane;
wxComboBox *cbRate;
wxComboBox *cbLogfiles;
wxButton *btnRotateLog;
ctlComboBoxFix *cbDatabase;
int statusSortColumn;
wxString statusSortOrder;
int lockSortColumn;
wxString lockSortOrder;
int xactSortColumn;
wxString xactSortOrder;
wxTimer *refreshUITimer;
wxTimer *statusTimer, *locksTimer, *xactTimer, *logTimer, *querystateTimer;
int statusRate, locksRate, xactRate, logRate, querystateRate;
ctlListView *statusList;
ctlListView *lockList;
ctlListView *xactList;
ctlListView *logList;
ctlNavigatePanel* nav;
ctlListView *querystateList;
wxComboBox *cbRate;
wxComboBox *cbLogfiles;
wxButton *btnRotateLog;
ctlComboBoxFix *cbDatabase;
wxMenu *actionMenu;
wxMenu *statusPopupMenu;
wxMenu *lockPopupMenu;
wxMenu *xactPopupMenu;
wxMenu *querystatePopupMenu;
wxString queryplan;
wxArrayString queries;
wxArrayInt filterColumn;
wxArrayString filterValue;
wxTimer *refreshUITimer;
wxTimer *statusTimer, *locksTimer, *xactTimer, *logTimer, *querystateTimer;
int statusRate, locksRate, xactRate, logRate, querystateRate;
int statusColWidth[12], lockColWidth[10], xactColWidth[5], querystateColWidth[5];
ctlListView *statusList;
ctlListView *lockList;
ctlListView *xactList;
ctlListView *logList;
ctlListView *querystateList;
int cboToRate();
wxString rateToCboString(int rate);
wxMenu *actionMenu;
wxMenu *statusPopupMenu;
wxMenu *lockPopupMenu;
wxMenu *xactPopupMenu;
wxMenu *querystatePopupMenu;
wxString queryplan;
wxArrayString queries;
wxArrayInt filterColumn;
wxArrayString filterValue;
int statusColWidth[12], lockColWidth[10], xactColWidth[5], querystateColWidth[5];
int cboToRate();
wxString rateToCboString(int rate);
wxImageList *listimages;
long getlongvalue(wxString source,wxString match_str);
void AddStatusPane();
void AddLockPane();
void AddXactPane();
void AddLogPane();
wxImageList *listimages;
long getlongvalue(wxString source,wxString match_str);
void AddStatusPane();
void AddLockPane();
void AddXactPane();
void AddLogPane();
void AddQuerystatePane();
void OnHelp(wxCommandEvent &ev);
void OnContents(wxCommandEvent &ev);
void OnExit(wxCommandEvent &event);
void OnHelp(wxCommandEvent &ev);
void OnContents(wxCommandEvent &ev);
void OnExit(wxCommandEvent &event);
void OnCopy(wxCommandEvent &ev);
void OnCopyQuery(wxCommandEvent &ev);
void OnCopy(wxCommandEvent &ev);
void OnCopyQuery(wxCommandEvent &ev);
void OnToggleStatusPane(wxCommandEvent &event);
void OnToggleLockPane(wxCommandEvent &event);
void OnToggleXactPane(wxCommandEvent &event);
void OnToggleLogPane(wxCommandEvent &event);
void OnToggleQuerystatePane(wxCommandEvent &event);
void OnEmptyAction(wxCommandEvent &event);
void OnToggleToolBar(wxCommandEvent &event);
void OnDefaultView(wxCommandEvent &event);
void OnHighlightStatus(wxCommandEvent &event);
void OnToggleStatusPane(wxCommandEvent &event);
void OnToggleLockPane(wxCommandEvent &event);
void OnToggleXactPane(wxCommandEvent &event);
void OnToggleLogPane(wxCommandEvent &event);
void OnToggleQuerystatePane(wxCommandEvent &event);
void OnToggleWaitEnable(wxCommandEvent& event);
void OnEmptyAction(wxCommandEvent &event);
void OnLogContextMenu(wxCommandEvent& event);
void OnRefreshUITimer(wxTimerEvent &event);
void OnRefreshStatusTimer(wxTimerEvent &event);
void OnRefreshLocksTimer(wxTimerEvent &event);
void OnRefreshXactTimer(wxTimerEvent &event);
void OnRefreshLogTimer(wxTimerEvent &event);
void OnRefreshQuerystateTimer(wxTimerEvent &event);
void OnToggleToolBar(wxCommandEvent &event);
void OnDefaultView(wxCommandEvent &event);
void OnHighlightStatus(wxCommandEvent &event);
void SetColumnImage(ctlListView *list, int col, int image);
void OnSortStatusGrid(wxListEvent &event);
void OnRightClickStatusItem(wxListEvent& event);
void OnSortLockGrid(wxListEvent &event);
void OnSortXactGrid(wxListEvent &event);
void OnRefreshUITimer(wxTimerEvent &event);
void OnRefreshStatusTimer(wxTimerEvent &event);
void OnRefreshLocksTimer(wxTimerEvent &event);
void OnRefreshXactTimer(wxTimerEvent &event);
void OnRefreshLogTimer(wxTimerEvent &event);
void OnRefreshQuerystateTimer(wxTimerEvent &event);
void OnRightClickStatusGrid(wxListEvent &event);
void OnRightClickLockGrid(wxListEvent &event);
void OnRightClickXactGrid(wxListEvent &event);
void OnRightClickQuerystateGrid(wxListEvent &event);
void SetColumnImage(ctlListView *list, int col, int image);
void OnSortStatusGrid(wxListEvent &event);
void OnRightClickStatusItem(wxListEvent& event);
void OnSortLockGrid(wxListEvent &event);
void OnSortXactGrid(wxListEvent &event);
void OnStatusMenu(wxCommandEvent &event);
void OnLockMenu(wxCommandEvent &event);
void OnXactMenu(wxCommandEvent &event);
void OnQuerystateMenu(wxCommandEvent &event);
void OnRightClickStatusGrid(wxListEvent &event);
void OnRightClickLockGrid(wxListEvent &event);
void OnRightClickXactGrid(wxListEvent &event);
void OnRightClickQuerystateGrid(wxListEvent &event);
void OnRightClickLogGrid(wxListEvent& event);
void OnStatusMenu(wxCommandEvent &event);
void OnLockMenu(wxCommandEvent &event);
void OnXactMenu(wxCommandEvent &event);
void OnQuerystateMenu(wxCommandEvent &event);
void OnChgColSizeStatusGrid(wxListEvent &event);
void OnChgColSizeLockGrid(wxListEvent &event);
void OnChgColSizeXactGrid(wxListEvent &event);
void OnChgColSizeQuerystateGrid(wxListEvent &event);
void OnRateChange(wxCommandEvent &event);
void OnChgColSizeStatusGrid(wxListEvent &event);
void OnChgColSizeLockGrid(wxListEvent &event);
void OnChgColSizeXactGrid(wxListEvent &event);
void OnChgColSizeQuerystateGrid(wxListEvent &event);
void OnRateChange(wxCommandEvent &event);
void OnPaneClose(wxAuiManagerEvent &evt);
void OnPaneClose(wxAuiManagerEvent &evt);
void OnPaneActivated(wxAuiManagerEvent& evt);
void OnClose(wxCloseEvent &event);
void OnRefresh(wxCommandEvent &event);
void OnCancelBtn(wxCommandEvent &event);
void OnStatusCancelBtn(wxCommandEvent &event);
void OnLocksCancelBtn(wxCommandEvent &event);
void OnTerminateBtn(wxCommandEvent &event);
void OnStatusTerminateBtn(wxCommandEvent &event);
void OnLocksTerminateBtn(wxCommandEvent &event);
void OnSelStatusItem(wxListEvent &event);
void OnSelLockItem(wxListEvent &event);
void OnSelXactItem(wxListEvent &event);
void OnSelLogItem(wxListEvent &event);
void OnSelQuerystateItem(wxListEvent &event);
void OnLoadLogfile(wxCommandEvent &event);
void OnRotateLogfile(wxCommandEvent &event);
void OnCommit(wxCommandEvent &event);
void OnRollback(wxCommandEvent &event);
void OnClearFilter(wxCommandEvent& event);
void OnClose(wxCloseEvent &event);
void OnRefresh(wxCommandEvent &event);
void OnCancelBtn(wxCommandEvent &event);
void OnStatusCancelBtn(wxCommandEvent &event);
void OnLocksCancelBtn(wxCommandEvent &event);
void OnTerminateBtn(wxCommandEvent &event);
void OnStatusTerminateBtn(wxCommandEvent &event);
void OnLocksTerminateBtn(wxCommandEvent &event);
void OnSelStatusItem(wxListEvent &event);
void OnSelLockItem(wxListEvent &event);
void OnSelXactItem(wxListEvent &event);
void OnSelLogItem(wxListEvent &event);
void OnSelQuerystateItem(wxListEvent &event);
void OnLoadLogfile(wxCommandEvent &event);
void OnRotateLogfile(wxCommandEvent &event);
void OnCommit(wxCommandEvent &event);
void OnRollback(wxCommandEvent &event);
void OnClearFilter(wxCommandEvent& event);
void OnLogKeyUp(wxKeyEvent& event);
void OnAddLabelTextThread(wxThreadEvent& event);
void ActivatePane(wxString name);
void OnChangeDatabase(wxCommandEvent &ev);
void OnChangeDatabase(wxCommandEvent &ev);
int fillLogfileCombo();
void emptyLogfileCombo();
int fillLogfileCombo();
void emptyLogfileCombo();
void addLogFile(wxDateTime *dt, bool skipFirst);
void addLogFile(const wxString &filename, const wxDateTime timestamp, long len, long &read, bool skipFirst);
void addLogLine(const wxString &str, bool formatted = true, bool csv_log_format = false);
void addLogFile(wxDateTime *dt, bool skipFirst);
void addLogFile(const wxString &filename, const wxDateTime timestamp, long len, long &read, bool skipFirst);
void addLogLine(const wxString &str, bool formatted = true, bool csv_log_format = false);
void checkConnection();
void checkConnection();
DECLARE_EVENT_TABLE()
DECLARE_EVENT_TABLE()
};
class serverStatusFactory : public actionFactory
{
public:
serverStatusFactory(menuFactoryList *list, wxMenu *mnu, ctlMenuToolbar *toolbar);
wxWindow *StartDialog(frmMain *form, pgObject *obj);
bool CheckEnable(pgObject *obj);
serverStatusFactory(menuFactoryList *list, wxMenu *mnu, ctlMenuToolbar *toolbar);
wxWindow *StartDialog(frmMain *form, pgObject *obj);
bool CheckEnable(pgObject *obj);
};
#endif

195
include/utils/WaitSample.h Normal file
View file

@ -0,0 +1,195 @@
#pragma once
#include <map>
struct Sample
{
int btime;
int etime;
int wait_id;
int pid;
int btype; // backend_type = 0 Client backend
long long qid;
int samples;
};
enum iswhat {
WAIT_FULL,
WAIT_GRP,
WAIT_NAME
} ;
/// <summary>
/// qid,wait_id,pid
/// </summary>
struct key3 {
long long qid;
int w, pid;
int sum;
bool const operator==(const key3& o) const {
return qid == o.qid && w == o.w && pid==o.pid;
}
bool const cmp2field(const key3& o) const {
return qid == o.qid && w == o.w;
}
bool const operator<(const key3& o) const {
return qid < o.qid || (qid == o.qid && w < o.w)|| (qid == o.qid && w == o.w && pid<o.pid);
}
};
/// <summary>
/// compare pid,wait,qid
/// </summary>
struct key3p {
long long qid;
int w, pid;
int sum;
bool const operator==(const key3p& o) const {
return qid == o.qid && w == o.w && pid == o.pid;
}
bool const cmp2field(const key3p& o) const {
return qid == o.qid && w == o.w;
}
bool const operator<(const key3p& o) const {
return pid < o.pid || (pid == o.pid && w < o.w) || (pid == o.pid && w == o.w && qid < o.qid);
}
};
typedef std::vector<int> vec_int;
class WaitSample
{
private:
int periodms = 10;
int history_size;
wxJSONValue opt;
std::map<int, int> pids;
std::map<int, int> chkpids;
//
std::vector<wxString> btype = { "client backend","background writer","checkpointer"
,"walreceiver","walsender","walwriter","autovacuum launcher","autovacuum worker"
,"logical replication launcher","logical replication worker","parallel worker"
,"archiver","startup" };
std::vector<wxString> btypeshort = { "CL","BGW","CKP"
,"walR","walS","walW","avL","avW"
,"lrL","lrW","pW"
,"ARC","startup" };
std::map<long long, wxString> qid_sql;
std::map<wxString, long> wait_idx; // mapping wait_name -> color rgb
std::map<wxString, int> wait_disable;
std::map<wxString, int> wait_id;
std::map<int, wxString> wait_id_name; // number wait -> full name wait
std::vector<int> colors; // mapping colors[wait_id] -> color rgb
std::vector<wxString> group;
std::vector<Sample> smp; //
std::vector<Sample> tmp_smp; //
long long basetime = 0;
int timebegiserios = 0, timeendserios = 0;
int start_index_serios;
/// <summary>
/// Получение номера группы по имени группы.
/// </summary>
/// <param name="wclass"></param>
/// <returns></returns>
int wait_class(wxString wclass);
public:
void SaveFileSamples();
void LoadFileSamples();
bool AddQuery(long long qid, wxString sql);
wxString GetQueryByQid(long long qid);
/// <summary>
/// Получить Id backend_type
/// Если неизвестный backend_type то он получит новый ID
/// </summary>
/// <param name="backend_type"></param>
/// <returns></returns>
int GetBackendTypeId(wxString backend_type);
wxString GetBackendTypeName(int backend_id) {
return btype[backend_id];
};
wxString GetBackendTypeNameShort(int backend_id) {
return backend_id>=btypeshort.size() ? GetBackendTypeName(backend_id): btypeshort[backend_id];
};
/// <summary>
/// Получение индекса цвета по полному имени ожидания
/// Если для ожидание нет цвета то используется цвет группы
/// </summary>
/// <param name="wait_name">Полное имя ожидания</param>
/// <returns>Индекс в массиве colors</returns>
long GetColorByWaitName(wxString wait_name) {
auto clr = wait_idx.find(wait_name);
if (clr == wait_idx.end()) {
wxString grp_name = wait_name.BeforeFirst(':');
clr = wait_idx.find(grp_name);
}
return clr->second;
};
/// <summary>
/// Получение Текстового имени ожидания
/// </summary>
/// <param name="wait_id">Ид ожидания</param>
/// <param name="isGrp">WAIT_FULL - полное имя,WAIT_GRP - группа, WAIT_NAME - имя ожидания </param>
/// <returns></returns>
wxString GetName(int wait_id, iswhat isGrp) {
wxString n = wait_id_name[wait_id];
if (isGrp == WAIT_FULL) return n;
wxString grp_name = n.BeforeFirst(':');
if (grp_name.IsEmpty()) return n;
if (isGrp==WAIT_GRP) return grp_name;
else
return n.AfterFirst(':');
};
int GetCountWaits() { return wait_id.size(); }
inline int d_time(long long ms) {
return ms - basetime;
}
std::vector<Sample>* GetSamples();
std::vector<int> GetColors();
/// <summary>
/// Получение начальной границы интервала для переданного времени.
/// </summary>
/// <param name="timeEnd">-1 текущая левая граница, или время</param>
/// <param name="AggrigateInterval"> Агрегирующий интервал</param>
/// <returns>Время начала интервала.</returns>
int GetHomeInterval(int timeEnd, int AggrigateInterval);
int GetInterval(int posStart, int posEnd);
wxArrayString GetWaitIdNameArray(bool onlyGroup);
int GetGroupingData(int timeEnd, // -1 для правой границы
int needCountGroup, // сколько интервалов получить
int groupInterval, // 1 min, 5 min, 10 min, 20 min, 30 min, 1 hour
wxString groupRule, // как проводить групировку
wxArrayString& nameGroup, // эаполняет именами ожиданий или групп ожиданий
std::vector<wxDateTime>& xAxisValue,// границы интервалов
std::vector<wxTimeSpan>& yAxisValue,
std::vector<vec_int>& Values,
std::vector<long>& clr
);
/// <summary>
/// Получение позиции в массиве по указанному времени
/// </summary>
/// <param name="time"></param>
/// <returns></returns>
int getPositionByTime(int time);
inline int getPeriod() { return periodms; }
void SetConfig(long periodmills, long history_size) {
periodms = periodmills;
this->history_size = history_size;
};
void Init();
WaitSample() {
//c2.FromString("#3644ff");
Init();
};
void BeginSeriosSample(long long timeserios) {
chkpids.clear();
if (basetime == 0) basetime = timeserios;
timebegiserios = timeendserios;
timeendserios = d_time(timeserios);
start_index_serios = smp.size();
}
void EndSeriosSample();
void AddSample(int pid, bool isXidTransation, wxString& active, const wxString& sample);
};

File diff suppressed because it is too large Load diff

714
utils/WaitSample.cpp Normal file
View file

@ -0,0 +1,714 @@
#include "pgAdmin3.h"
#include "utils/WaitSample.h"
#include "wx/stdpaths.h"
#include "utils/utffile.h"
#include "utils/json/jsonval.h"
void WaitSample::Init() {
wxString clr = "h1 { \
region: \"IO:DataFileRead\", #3644ff;\n\
region: \"IO:DataFileWrite\", #790808;\n\
region: \"IO:BufFileRead\", #16658d;\n\
region: \"IO:BufFileWrite\", #d30a0a;\n\
region: \"IO:DataFileExtend\", #9720ba;\n\
region: \"IO:DataFilePrefetch\", #c03ae8;\n\
region: \"IO:DataFileFlush\", #b609ea;\n\
region: \"IO:DataFileSync\", #b609ea;\n\
region: \"IO:ReplicationSlotSync\", #b609ea;\n\
region: \"IO:WALSync\", #ff6a00;\n\
region: \"IO:WALWrite\", #ff6a00;\n\
region: \"IO:WALInitWrite\", #ff6a00;\n\
region: \"IO:WALRead\", #ba550e;\n\
region: \"IO\", #2132bd;\n\
region: \"IPC\", #908b3b;\n\
region: \"Lock\", #ff0000;\n\
region: \"Client\", #0b6222;\n\
region: \"BufferPin\", #a4a3a0;\n\
region: \"Client:ClientWrite\", #76bb88;\n\
region: \"IPC:ArchiveCommand\", #fff200;\n\
region: \"IPC:MessageQueueReceive\", #aaae4f;\n\
region: \"IPC:MessageQueueSend\", #aaae4f;\n\
region: \"LWLock\", #87f566;\n\
region: \"LWLock:WALWrite\", #3bf61e;\n\
region: \"LWLock:WALInsert\", #3bf61e;\n\
region: \"LWLock:Autovacuum\", #20bb08;\n\
region: \"LWLock:BufferContent\", #98db19;\n\
region: \"Timeout\", #6ce4c6;\n\
region: \"Timeout:VacuumDelay\", #20bb08;\n\
}";
wxStringTokenizer tk(clr, "\n", wxTOKEN_DEFAULT);
wxString cc;
wxString cnt;
wxString ee;
wxString l;
wxJSONValue events(wxJSONType::wxJSONTYPE_ARRAY);
while (tk.HasMoreTokens())
{
wxString l = tk.GetNextToken();
wxString w = l.AfterFirst('"').BeforeFirst('"');
if (w.IsEmpty()) continue;
wxString c = l.AfterFirst('#').BeforeFirst(';');
unsigned long tmp;
wxSscanf(c, "%lx", &tmp);
wait_idx.emplace(w, tmp);
wxJSONValue e(wxJSONType::wxJSONTYPE_OBJECT);
wxColour cc3("#"+c);
e["enable"] = true;
e["color"] = "#" + c;
e["name"] = w;
events.Append(e);
}
wxJSONValue def(wxJSONType::wxJSONTYPE_OBJECT);
def["events"] = events;
//def["autoloadcache_sql"] = true;
group.push_back("BufferPin");
group.push_back("Client");
group.push_back("IO");
group.push_back("IPC");
group.push_back("Lock");
group.push_back("Activity");
group.push_back("LWLock");
group.push_back("Timeout");
settings->ReloadJsonFileIfNeed();
settings->ReadJsonObect("WaitEvents", opt, def);
// settings->WriteJsonFile();
if (!opt.IsNull()) {
wait_idx.clear();
wxJSONValue r = opt["events"];
for (int j = 0; j < r.Size(); j++) {
wxJSONValue e = r[j];
bool enable = e["enable"].AsBool();
wxString c= e["color"].AsString();
unsigned long tmp;
wxSscanf(c, "#%lx", &tmp);
wxColour cc(tmp);
if (!cc.IsOk()) cc = *wxBLACK;
//wxSscanf(c, "%lx", &tmp);
wxString w = e["name"].AsString();
wait_idx.emplace(w, tmp);
if (!enable) wait_disable.emplace(w,0);
}
}
else opt = def;
wxString tempDir = wxStandardPaths::Get().GetUserConfigDir() + wxFileName::GetPathSeparator() + "postgresql" + wxFileName::GetPathSeparator() + "cache_sql.txt";
wxTextFile file(tempDir);
if (file.Exists()) file.Open();
if (file.IsOpened())
{
wxString str;
wxString sql;
long long qid;
for (str = file.GetFirstLine(); !file.Eof(); str = file.GetNextLine())
{
if (str.length() == 16) {
unsigned long long temp_qid = 0;
int cntbits = 0;
for (int i = str.length() - 1; i >= 0; i--, cntbits = cntbits + 4)
{
unsigned char h = str[i];
unsigned char n09 = h - '0';
unsigned char nAF = (h | 0x20) - 'a';
if ((n09 <= (9 - 0)) || (nAF <= (0xf - 0xa))) {
// is hex digits
}
else break;
unsigned long long tetra = ((h & 0xf) + (h >> 6) * 9);
temp_qid |= tetra << cntbits;
}
if (cntbits == 64) {
//wxString tmp = sql.BeforeLast('\n');
//if (tmp.IsEmpty()) tmp=sql;
if (sql.length() > 0) AddQuery(qid, sql); // previos sql
qid = temp_qid;
sql = wxEmptyString;
continue;
}
}
if (!sql.IsEmpty()) sql.Append('\n');
sql.Append(str);
}
//file.ReadAll(&str);
file.Close();
if (sql.length() > 0)
{
//wxString tmp = sql.BeforeLast('\n');
//if (tmp.IsEmpty()) tmp = sql;
AddQuery(qid, sql); // previos sql
}
}
}
std::vector<Sample>* WaitSample::GetSamples() { return &smp; }
std::vector<int> WaitSample::GetColors() { return colors; }
wxArrayString WaitSample::GetWaitIdNameArray(bool onlyGroup) {
typedef std::pair<wxString, int> pair;
// sort map by value;
std::vector<pair> vec;
std::copy(wait_id.begin(),
wait_id.end(),
std::back_inserter<std::vector<pair>>(vec));
std::sort(vec.begin(), vec.end(),
[](const pair& l, const pair& r)
{
if (l.second != r.second) {
return l.second < r.second;
}
return l.first < r.first;
});
wxArrayString a;
for (auto const& pair : vec) {
//std::cout << '{' << pair.first << "," << pair.second << '}' << std::endl;
wxString n = pair.first;
if (onlyGroup) {
if (!n.BeforeFirst(':').IsEmpty()) n = n.BeforeFirst(':');
}
a.Add(n);
}
return a;
}
// return position in smp vector
int WaitSample::GetHomeInterval(int timeEnd, int AggrigateInterval) {
Sample sa;
if (timeEnd == -1) {
// end smp
sa = smp.back();
timeEnd = sa.btime;
}
else {
}
long long th = ((basetime + timeEnd) / AggrigateInterval) * AggrigateInterval;
int home_int = th - basetime;
if (th > (basetime + timeEnd)) {
//long long te = th - AggrigateInterval;
home_int = th - AggrigateInterval - basetime;
}
return home_int;
//wxLongLong l(((basetime + timeEnd)/AggrigateInterval)* AggrigateInterval);
//wxDateTime t(l);
}
int WaitSample::getPositionByTime(int time) {
int max = smp.size() - 1;
int min = 0;
int i = (max - min) / 2;
while (i != -1) {
int t = smp[i].btime;
if (t < time) {
min = i;
i = min + (max - min) / 2;
}
else if (t > time) {
max = i;
i = min + (max - min) / 2;
}
else {
while (i > 0 && smp[i - 1].btime == t) {
i--;
}
break;
}
if (max - min < 2) {
i = max;
break;
}
}
return i;
}
int WaitSample::wait_class(wxString wclass) {
int i = 0;
for (const auto& name : group) {
if (name == wclass) return i;
i++;
}
wxFAIL_MSG("Unknown wait class " + wclass);
return -1000;
}
// return count group
int WaitSample::GetGroupingData(int timeEnd, int needCountGroup, int groupInterval,
wxString groupRule, wxArrayString& nameGroup,
std::vector<wxDateTime>& xAxisValue,
std::vector<wxTimeSpan>& yAxisValue,
std::vector<vec_int>& Values, // ìàññèâ ñòîëáöîâ ñïðàâà íà ëåâî
std::vector<long>& clr // öåòà ñëî¸â ðàçìåðíîñòüþ groupRule.Count()
)
{
int idx_grp = wait_id.size();
std::vector<int> filter_map(wait_id.size() + group.size());
std::vector<int> summary(wait_id.size() + group.size());
for (size_t i = 0; i < filter_map.size(); i++) filter_map[i] = -1;
nameGroup.Clear();
//std::map<int, wxString> wait_id_name;
long long qidfilter = -1;
for (const auto& kv : wait_id) {
//std::cout << kv.first << " has value " << kv.second << std::endl;
wxString w_name = kv.first;
int w_id = kv.second;
//wait_id_name.emplace(w_id, w_name);
bool enable = true;
wxString grp_name = w_name.BeforeFirst(':');
if (grp_name.IsEmpty()) grp_name = w_name;
int cl = wait_class(grp_name);
bool isF = false;
if (!groupRule.IsEmpty()) {
wxStringTokenizer tk(groupRule, ";", wxTOKEN_DEFAULT);
wxString l;
bool isDis = true;
bool isGrp = true;
while (tk.HasMoreTokens())
{
l = tk.GetNextToken();
if (l[0] == '@') {
if (qidfilter == -1) {
int cntbits = 0;
unsigned long long temp_qid = 0;
for (int i = l.length() - 1; i > 0; i--, cntbits = cntbits + 4)
{
unsigned char h = l[i];
unsigned char n09 = h - '0';
unsigned char nAF = (h | 0x20) - 'a';
if ((n09 <= (9 - 0)) || (nAF <= (0xf - 0xa))) {
// is hex digits
}
else break;
unsigned long long tetra = ((h & 0xf) + (h >> 6) * 9);
temp_qid |= tetra << cntbits;
}
qidfilter = temp_qid;
}
continue;
}
isDis = l[0] == '-';
if (isDis) l = l.substr(1);
isGrp = l.find(':') == -1;
if (w_name == l) {
if (isDis) {
filter_map[w_id] = w_id; //disable in Grp
if (filter_map[idx_grp + cl] < 0) filter_map[idx_grp + cl] = -2;
}
else {
//filter_map[w_id] = w_id;
isF = false; //default
break;
}
isF = true;
break;
}
if (isGrp) {
if (grp_name == l) {
if (isDis) {
// Groups equal and disable
filter_map[w_id] = w_id; // disable w_id Grp ALL disable
filter_map[idx_grp + cl] = -2; // disable group
isF = true;
break;
}
// default rule
break;
}
continue; // next rule
}
}
}
if (!isF) {
// no found in Rule
filter_map[w_id] = idx_grp + cl; // wait_id -> [idx_grp + cl]
filter_map[idx_grp + cl] = 999; // visible
//filter_map[idx_grp+cl]=
}
}
// nameGroup
// begin Group
wxString n, g;
clr.clear();
clr.resize(filter_map.size());
for (size_t i = idx_grp; i < filter_map.size(); i++)
{
int wait_id = filter_map[i];
if (wait_id == -1) continue;
n = group[i - idx_grp];
g = n;
clr[i] = GetColorByWaitName(n);
if (wait_id == -2) n = '-' + n;
nameGroup.Add(n);
// individual wait
for (size_t j = 0; j < idx_grp; j++)
{
int wait_id = filter_map[j];
n = wait_id_name[j];
wxString grp_name = n.BeforeFirst(':');
if (grp_name.IsEmpty()) grp_name = n;
if (grp_name != g) continue;
if (!n.AfterFirst(':').IsEmpty()) n = n.AfterFirst(':');
if (wait_id == j) n = "--" + n; else n = " " + n;
nameGroup.Add(n);
clr[j] = GetColorByWaitName(wait_id_name[j]);
}
}
int homeInt = GetHomeInterval(timeEnd, groupInterval);
Sample sa;
if (timeEnd == -1) {
sa = smp.back();
timeEnd = sa.btime;
}
int idx = getPositionByTime(timeEnd);
for (size_t i = 0; i < summary.size(); i++) summary[i] = 0;
Values.clear();
xAxisValue.clear();
//for (size_t i = 0; i < maxS.size(); i++) maxS[i] = 0;
long long maxsum = 0;
int minsum = 0;
int nGrp = needCountGroup;
while (idx >= 0) {
sa = smp[idx--];
if (sa.btime >= homeInt && idx >= 0) {
}
else {
// new interval
wxLongLong l(basetime + homeInt);
wxDateTime t(l);
xAxisValue.push_back(t);
std::vector<int> itog(summary.size());
int ni = idx_grp;
int Ymax = 0;
for (size_t i = 0; i < itog.size(); i++)
{
int wait_id = filter_map[i];
if (wait_id < 0) { itog[i] = wait_id; continue; }
int a = summary[i];
itog[i] = a;
if (i >= idx_grp) Ymax += a;
//if (minsum > a) minsum = a;
}
if (maxsum < Ymax) maxsum = Ymax;
Values.push_back(itog);
homeInt -= groupInterval;
nGrp--;
if (nGrp == 0) break;
for (size_t i = 0; i < summary.size(); i++) summary[i] = 0;
}
if (qidfilter != -1 && sa.qid != qidfilter) continue;
int w_id = sa.wait_id;
if (filter_map[w_id] < 0) continue;
int ii = filter_map[w_id];
// grp
int s = sa.samples * periodms;
if (ii != w_id) summary[ii] += s;
// details
summary[w_id] += s;
}
yAxisValue.clear();
yAxisValue.push_back(wxTimeSpan(0, 0, 0, maxsum));
return Values.size();
}
int WaitSample::GetInterval(int posStart, int posEnd) {
Sample sa1, sa2;
if (smp.size() == 0) return 0;
if (posEnd == -1) {
sa2 = smp.back();
}
else sa2 = smp.at(posEnd);
sa1 = smp.at(posStart);
return sa2.btime - sa1.btime;
}
bool WaitSample::AddQuery(long long qid, wxString sql) {
if (qid_sql.find(qid) == qid_sql.end()) {
qid_sql.emplace(qid, sql);
return true;
}
return false;
}
wxString WaitSample::GetQueryByQid(long long qid) {
if (qid_sql.find(qid) != qid_sql.end()) {
wxString sql = qid_sql[qid];
return sql;
}
return wxEmptyString;
}
void WaitSample::LoadFileSamples() {
wxString tempDir = wxStandardPaths::Get().GetUserConfigDir() + wxFileName::GetPathSeparator() + "postgresql" + wxFileName::GetPathSeparator() + "sample.dat";
wxTextFile file(tempDir);
file.Open();
if (file.IsOpened())
{
wxString str;
for (str = file.GetFirstLine() + '\n'; !file.Eof(); str += file.GetNextLine() + '\n')
{
}
//file.ReadAll(&str);
file.Close();
wxStringTokenizer tk(str, "\n", wxTOKEN_DEFAULT);
wxString cc;
wxString cnt;
wxString ee;
wxString l;
int nl = 0;
while (tk.HasMoreTokens())
{
wxString l = tk.GetNextToken();
if (l.IsEmpty()) continue;
if (nl == 0) {
long long t;
wxSscanf(l, "%lld", &basetime);
nl++;
continue;
}
if (nl == 4) {
//smp
//wxString w_name = tk.GetNextToken();
//wxString w_id = tk2.GetNextToken();
int tmp = 0;
Sample sa;
if (wxSscanf(l, "%d %d %d %d %lld %d %d", &sa.btime, &sa.etime, &sa.wait_id, &sa.pid, &sa.qid, &sa.btype, &sa.samples) != 7) {
wxLogError("Invalid count parameters '%s'.", l);
}
/*wxSscanf(tk2.GetNextToken(), "%d", &sa.btime);
wxSscanf(tk2.GetNextToken(), "%d", &sa.etime);
wxSscanf(tk2.GetNextToken(), "%d", &sa.wait_id);
wxSscanf(tk2.GetNextToken(), "%d", &sa.pid);
wxSscanf(tk2.GetNextToken(), "%lld", &sa.qid);
wxSscanf(tk2.GetNextToken(), "%d", &sa.btype);
wxSscanf(tk2.GetNextToken(), "%d", &sa.samples);*/
smp.push_back(sa);
continue;
}
wxStringTokenizer tk2(l, " ", wxTOKEN_DEFAULT);
if (nl == 1) {
//btype
while (tk2.HasMoreTokens())
{
wxString b_name, b_id;
while (b_id.IsEmpty()) {
wxString str = tk2.GetNextToken();
if (!wxIsdigit(str[0]))
b_name += b_name.IsEmpty() ? str : " " + str;
else b_id = str;
}
unsigned long tmp;
wxSscanf(b_id, "%ld", &tmp);
int i = GetBackendTypeId(b_name);
if (i != tmp) {
}
}
nl++;
continue;
}
if (nl == 2) {
//wait_id
while (tk2.HasMoreTokens())
{
wxString w_name, w_id;
while (w_id.IsEmpty()) {
wxString str = tk2.GetNextToken();
if (!wxIsdigit(str[0]))
w_name += w_name.IsEmpty() ? str : " " + str;
else w_id = str;
}
unsigned long tmp;
wxSscanf(w_id, "%ld", &tmp);
wait_id.emplace(w_name, tmp);
wait_id_name.emplace(tmp, w_name);
}
nl++;
continue;
}
if (nl == 3) {
//colors
int sz = wait_id.size();
colors.resize(sz);
int i = 0;
while (tk2.HasMoreTokens())
{
wxString c = tk2.GetNextToken();
unsigned long tmp;
wxSscanf(c, "%ld", &tmp);
colors[i++] = tmp;
}
nl++;
continue;
}
}
}
}
void WaitSample::SaveFileSamples() {
wxString tempDir = wxStandardPaths::Get().GetUserConfigDir() + wxFileName::GetPathSeparator() + "postgresql" + wxFileName::GetPathSeparator() + "sample.dat";
wxUtfFile file(tempDir, wxFile::write, wxFONTENCODING_UTF8);
if (file.IsOpened())
{
wxString strnl;
strnl = wxString::Format("%lld\n", basetime);
file.Write(strnl);
strnl.Clear();
//btype
int i = 0;
for (const auto& e : btype) {
if (!strnl.IsEmpty()) strnl += ' ';
strnl += wxString::Format("%s %d", e, i);
i++;
}
strnl += '\n';
file.Write(strnl);
// wait_id
strnl.Clear();
for (const auto& e : wait_id) {
if (!strnl.IsEmpty()) strnl += ' ';
strnl += wxString::Format("%s %d", e.first, e.second);
}
strnl += '\n';
file.Write(strnl);
strnl.Clear();
for (const auto& e : colors) {
if (!strnl.IsEmpty()) strnl += ' ';
strnl += wxString::Format("%d", e);
}
strnl += '\n';
file.Write(strnl);
strnl.Clear();
for (const auto& e : smp) {
strnl.Append(wxString::Format("%d %d %d %d %lld %d %d\n", e.btime, e.etime, e.wait_id, e.pid, e.qid, e.btype, e.samples));
}
file.Write(strnl);
file.Close();
}
tempDir = wxStandardPaths::Get().GetUserConfigDir() + wxFileName::GetPathSeparator() + "postgresql" + wxFileName::GetPathSeparator() + "cache_sql.txt";
wxUtfFile file1(tempDir, wxFile::write, wxFONTENCODING_UTF8);
if (file1.IsOpened())
{
wxString str;
for (const auto& e : qid_sql) {
wxLongLong l(e.first);
str = wxString::Format("%0x%0x\n%s\n", l.GetHi(), l.GetLo(), e.second);
file1.Write(str);
}
file1.Close();
}
}
void WaitSample::AddSample(int pid, bool isXidTransation, wxString& btype, const wxString& sample) {
//PidWait pw(pid, basetime);;
// ïîèñê
int pw;
auto iter = pids.find(pid);
if (iter != pids.end()) {
//std::cout << "Found the key " << iter->first << " with the value " << iter->second << "\n";
pw = iter->second;
}
else {
pids.emplace(pid, pw);
}
++chkpids[pid];
int bt = GetBackendTypeId(btype);
//pw.AddSample(sample, active, timebegiserios, timeendserios);
wxStringTokenizer tk(sample, ";", wxTOKEN_DEFAULT);
wxString cnt;
wxString w;
wxString et;
while (tk.HasMoreTokens())
{
wxString et = tk.GetNextToken();
wxString cnt = et.AfterLast(':');
et = et.BeforeLast(':');
wxString tqid = et.AfterLast(':');
wxString w = et.BeforeLast(':');
// ignore ClientRead out transaction
if (w == "Client:ClientRead" && !isXidTransation)
continue;
int smp_cnt;
long long qid;
wxSscanf(cnt, "%d", &smp_cnt);
wxSscanf(tqid, "%lld", &qid);
auto dis_it = wait_disable.find(w);
if (dis_it != wait_disable.end()) {
continue;
}
auto iter = wait_id.find(w);
int id;
if (iter == wait_id.end()) {
id = wait_id.size();
wait_id.emplace(w, id);
wait_id_name.emplace(id, w);
//map color
long c = 0;
auto it2 = wait_idx.find(w);
if (it2 == wait_idx.end()) {
it2 = wait_idx.find(w.BeforeFirst(':'));
if (it2 != wait_idx.end()) {
c = it2->second;
}
}
else c = it2->second;
colors.push_back(c);
//colors
}
else id = iter->second;
Sample sa;
sa.btime = timebegiserios;
sa.etime = timeendserios;
sa.wait_id = id;
sa.samples = smp_cnt;
sa.pid = pid;
sa.qid = qid;
sa.btype = bt;
smp.push_back(sa);
//pw.AddSample();
}
}
int WaitSample::GetBackendTypeId(wxString backend_type) {
int idx = 0;
auto it = std::find_if(btype.begin(), btype.end(), [&](const wxString& s) {
idx++;
return s == backend_type;
}
);
if (it != btype.end()) return idx - 1;
btype.push_back(backend_type);
return idx;
}
std::vector<int> tmp_idx_colors;
bool compareBySample(const Sample& a, const Sample& b)
{
if (tmp_idx_colors[a.wait_id] < tmp_idx_colors[b.wait_id])
return true;
if (tmp_idx_colors[b.wait_id] < tmp_idx_colors[a.wait_id])
return false;
return a.samples < b.samples;
}
void WaitSample::EndSeriosSample() {
int end_index = smp.size();
if ((end_index - start_index_serios) < 2) return;
// sort
//(std::list<int>::iterator it = C.begin(), end = C.end(); it != end; ++it)
std::vector<Sample>::iterator it = smp.end();
it = it - (end_index - start_index_serios);
tmp_idx_colors = colors;
std::sort(it, smp.end(), compareBySample);
return;
}