#include "pgAdmin3.h" #include "utils/WaitSample.h" #include "wx/stdpaths.h" #include "utils/utffile.h" #include "utils/json/jsonval.h" #include "frm/frmStatus.h" extern wxString dataDir; void WaitSample::Init() { wxString clr = "h1 { \ region: \"IO:DataFileRead\", #2132bd;\n\ region: \"IO:DataFileWrite\", #2132bd;\n\ region: \"IO:BufFileRead\", #16658d;\n\ region: \"IO:BufFileWrite\", #16658d;\n\ region: \"IO:DataFileExtend\", #9720ba;\n\ region: \"IO:DataFilePrefetch\", #2132bd;\n\ region: \"IO:DataFileFlush\", #2132bd;\n\ region: \"IO:DataFileSync\", #2132bd;\n\ region: \"IO:ReplicationSlotSync\", #b609ea;\n\ region: \"IO:WALSync\", #ff6a00;\n\ region: \"IO:WALWrite\", #ff6a00;\n\ region: \"IO:WALInitWrite\", #ff6a00;\n\ region: \"IO:WALRead\", #ff6a00;\n\ region: \"IO\", #2132bd;\n\ region: \"IPC\", #908b3b;\n\ region: \"Lock\", #ff0000;\n\ region: \"Activity\", #9fde72;\n\ region: \"Client\", #0b6222;\n\ region: \"BufferPin\", #a4a3a0;\n\ region: \"Client:ClientWrite\", #76bb88;\n\ region: \"IPC:ArchiveCommand\", #908b3b;\n\ region: \"IPC:MessageQueueReceive\", #aaae4f;\n\ region: \"IPC:MessageQueueSend\", #aaae4f;\n\ region: \"LWLock\", #87f566;\n\ region: \"LWLock:WALWrite\", #87f566;\n\ region: \"LWLock:WALInsert\", #87f566;\n\ region: \"LWLock:Autovacuum\", #87f566;\n\ region: \"LWLock:BufferContent\", #87f566;\n\ region: \"CPU\", #FFFA8A;\n\ region: \"Timeout\", #6ce4c6;\n\ region: \"Timeout:VacuumDelay\", #6ce4c6;\n\ region: \"Timeout:PgSleep\", #6ce4c6;\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=0; wxSscanf(c, "%lx", &tmp); tmp = (tmp >> 16) & 0xFF | (tmp & 0x00FF00) | (tmp & 0xFF) << 16; wait_idx.emplace(w, tmp); wxJSONValue e(wxJSONType::wxJSONTYPE_OBJECT); wxColour cc3; cc3.Set(tmp); bool en = true; if (w == "Timeout:PgSleep" || w == "Timeout:VacuumDelay" || w == "IPC:ArchiveCommand" || w == "Activity:RecoveryWalStream" || w == "Activity:WalReceiverMain" || w == "Activity:WalSenderMain" || w == "Timeout:PgSleep") en = false; // disable for example e["enable"] = en; e["color"] = "#" + c; e["name"] = w; events.Append(e); } wxJSONValue def(wxJSONType::wxJSONTYPE_OBJECT); def["events"] = events; wxString cstr; cstr = "#808080"; wxJSONValue c1=wxString(cstr); //BG wxJSONValue c2 = wxString("#000000"); //CURSOR_LINE cstr = "#c0c0c0"; wxJSONValue c3 = wxString(cstr); //GRID_LINE wxJSONValue c4 = wxString("#000000"); //LABEL def["bg"] = c1; def["cursorline"] = c2; def["gridline"] = c3; def["label"] = c4; //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"); group.push_back("CPU"); 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); tmp = (tmp >> 16) & 0xFF | (tmp & 0x00FF00) | (tmp & 0xFF) << 16; 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; // color gui wxColour cc1(opt["bg"].AsString()); if (cc1.IsOk()) color_gui.push_back(wxColour(cc1)); else color_gui.push_back(wxColour(def["bg"].AsString())); cc1.Set(opt["cursorline"].AsString()); if (cc1.IsOk()) color_gui.push_back(wxColour(cc1)); else color_gui.push_back(wxColour(def["cursor_line"].AsString())); cc1.Set(opt["gridline"].AsString()); if (cc1.IsOk()) color_gui.push_back(wxColour(cc1)); else color_gui.push_back(wxColour(def["gridline"].AsString())); cc1.Set(opt["label"].AsString()); if (cc1.IsOk()) color_gui.push_back(wxColour(cc1)); else color_gui.push_back(wxColour(def["label"].AsString())); wxString tempDir = dataDir + wxFileName::GetPathSeparator() + "cache_sql.txt"; m_file_cache_sql = tempDir; 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* WaitSample::GetSamples() { return &smp; } std::vector WaitSample::GetColors() { return colors; } wxArrayString WaitSample::GetWaitIdNameArray(bool onlyGroup) { typedef std::pair pair; // sort map by value; std::vector vec; std::copy(wait_id.begin(), wait_id.end(), std::back_inserter>(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) { if (time < 0) time = 0; 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& xAxisValue, std::vector& yAxisValue, std::vector& Values, // ������ �������� ������ �� ���� std::vector& clr // ���� ���� ������������ groupRule.Count() ) { int idx_grp = wait_id.size(); std::vector filter_map(wait_id.size() + group.size()); std::vector summary(wait_id.size() + group.size()); for (size_t i = 0; i < filter_map.size(); i++) filter_map[i] = -1; nameGroup.Clear(); //std::map 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+ groupInterval); wxDateTime t(l); xAxisValue.push_back(t); std::vector 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; } else { // try get text sql if (m_frmStatus && m_frmStatus->getTextSqlbyQid(qid)) { wxString sql = qid_sql[qid]; return sql; }; } return wxEmptyString; } void WaitSample::LoadFileSamples() { wxString tempDir = dataDir + 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) { 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 = dataDir + 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 = m_file_cache_sql; 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(); } } bool WaitSample::RemoveFiles() { bool rez = false; if (wxFileExists(m_file_cache_sql)) { wxRemoveFile(m_file_cache_sql); rez = true; } return rez; } 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 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::iterator it = C.begin(), end = C.end(); it != end; ++it) std::vector::iterator it = smp.end(); it = it - (end_index - start_index_serios); tmp_idx_colors = colors; std::sort(it, smp.end(), compareBySample); return; }