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

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);
};