/* * fm-search-uri.c * This file is part of qps -- Qt-based visual process status monitor * * Copyright 2015 Hong Jen Yee (PCMan) * Copyright 2012-2014 Andriy Grytsenko (LStranger) * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, * MA 02110-1301, USA. */ // for Non-ASCII locale //#include //#include // extern QTextCodec * codec ; //#define UniString(str) codec->toUnicode(str) int flag_24_ok; // we presume a kernel 2.4.x #include #include #include #include #include #include #include // COMMON bool Procview::treeview = 0; // true bool Procview::flag_show_file_path = false; bool Procview::flag_cumulative = false; // times cumulative with children's bool Procview::flag_pcpu_single = false; // %CPU= pcpu/num_cpus // COMMON: basic field int Procview::mini_fields[] = {F_PID, F_STAT, F_MEM, F_CPU, F_CMDLINE, F_END}; // COMMON? // return username from /etc/passwd char *userName(int uid, int euid) { char buff[128]; struct passwd *pw = getpwuid(uid); if (!pw) { // dont have name ! sprintf(buff, "%d", uid); } else strcpy(buff, pw->pw_name); if (uid != euid) strcat(buff, euid == 0 ? "*" : "+"); return strdup(buff); } // return group name (possibly numeric) char *groupName(int gid, int egid) { char *p; struct group *gr = getgrgid(gid); if (!gr) { p = (char *)malloc(11); sprintf(p, "%d", gid); } else p = strdup(gr->gr_name); // s.append("*"); return p; } Category::~Category() {} int Category::compare(Procinfo *a, Procinfo *b) { return string(a).compare(string(b)); } Cat_int::Cat_int(const char *heading, const char *explain, int w, int Procinfo::*member) : Category(heading, explain), int_member(member), field_width(w) { } QString Cat_int::string(Procinfo *p) { QString s; s.setNum(p->*int_member); return s; } int Cat_int::compare(Procinfo *a, Procinfo *b) { // qsort() only cares about the sign of the number returned by the // comparison function; only a subtraction is necessary return a->*int_member - b->*int_member; } // COMMON Cat_percent::Cat_percent(const char *heading, const char *explain, int w, float Procinfo::*member) : Category(heading, explain), float_member(member), field_width(w) { } QString Cat_percent::string(Procinfo *p) { QString s; s.sprintf("%01.2f", (double)(p->*float_member)); return s; } int Cat_percent::compare(Procinfo *a, Procinfo *b) { float at = a->*float_member, bt = b->*float_member; return at < bt ? 1 : (at > bt ? -1 : 0); } // added 2006/05/07 Cat_memory::Cat_memory(const char *heading, const char *explain, int w, unsigned long Procinfo::*member) : Category(heading, explain), uintl_member(member), field_width(w) { } QString Cat_memory::string(Procinfo *p) { QString s; char buff[128]; long sizeK, sizeM; sizeK = p->*uintl_member; sizeM = sizeK / 1024; /* if ( sizeM > 1024 ) { sprintf(buff,"%dM",sizeM/1024); } else */ if (sizeM > 0) { sprintf(buff, "%ldM", sizeM); } else sprintf(buff, "%ldK", sizeK); if (sizeK == 0) s = "0"; else s = buff; return s; } int Cat_memory::compare(Procinfo *a, Procinfo *b) { int bu = b->*uintl_member, au = a->*uintl_member; return bu >= au ? (bu == au ? 0 : 1) : -1; } Cat_uintl::Cat_uintl(const char *heading, const char *explain, int w, unsigned long Procinfo::*member) : Category(heading, explain), uintl_member(member), field_width(w) { } QString Cat_uintl::string(Procinfo *p) { QString s; s.setNum(p->*uintl_member); return s; } int Cat_uintl::compare(Procinfo *a, Procinfo *b) { int bu = b->*uintl_member, au = a->*uintl_member; return bu >= au ? (bu == au ? 0 : 1) : -1; } Cat_hex::Cat_hex(const char *heading, const char *explain, int w, unsigned long Procinfo::*member) : Cat_uintl(heading, explain, w, member) { } // QString.sprintf 2x speed than glibc.sprintf (by fasthyun@magicn.com) // but QString structuring & destructring eat more time QString Cat_hex::string(Procinfo *p) { QString s; s.sprintf("%8x", (unsigned)(p->*uintl_member)); return s; } // COMMON, Cat_swap::Cat_swap(const char *heading, const char *explain) : Category(heading, explain) { } QString Cat_swap::string(Procinfo *p) { QString s; // It can actually happen that size < resident (Sun under Solaris 2.6) // Possible with Linux ? long sizeK, sizeM; sizeK = p->size > p->resident ? p->size - p->resident : 0; sizeM = sizeK / 1024; if (sizeM > 0) s = QString::number(sizeM) + "M"; else s = QString::number(sizeK) + "K"; if (sizeK == 0) s = "0"; return s; } int Cat_swap::compare(Procinfo *a, Procinfo *b) { return (b->size - b->resident) - (a->size - a->resident); } Cat_string::Cat_string(const char *heading, const char *explain, QString Procinfo::*member) : Category(heading, explain), str_member(member) { } QString Cat_string::string(Procinfo *p) { return p->*str_member; } Cat_user::Cat_user(const char *heading, const char *explain) : Cat_string(heading, explain) { } QString Cat_user::string(Procinfo *p) { if (p->uid == p->euid) return Uidstr::userName(p->uid); else { QString s = Uidstr::userName(p->uid); s.append(p->euid == 0 ? "*" : "+"); return s; } } Cat_group::Cat_group(const char *heading, const char *explain) : Cat_string(heading, explain) { } QString Cat_group::string(Procinfo *p) { if (p->gid == p->egid) return Uidstr::groupName(p->gid); else { QString s = Uidstr::groupName(p->gid); s.append("*"); return s; } } Cat_wchan::Cat_wchan(const char *heading, const char *explain) : Cat_string(heading, explain) { } QString Cat_wchan::string(Procinfo *p) { #ifdef LINUX return p->wchan_str; #else return Wchan::name(p->wchan); #endif } Cat_cmdline::Cat_cmdline(const char *heading, const char *explain) : Cat_string(heading, explain) { } QString Cat_cmdline::string(Procinfo *p) { if (p->cmdline.isEmpty()) { QString s("("); s.append(p->command); s.append(")"); return s; } else { if (Procview::flag_show_file_path) return p->cmdline; else { QString s(p->cmdline); int i = s.indexOf(' '); if (i < 0) i = s.length(); if (i > 0) { i = s.lastIndexOf('/', i - 1); if (i >= 0) s.remove(0, i + 1); } return s; } } } // hmm COMMON? almost same...but Solaris's zombie process... Cat_start::Cat_start(const char *heading, const char *explain) : Category(heading, explain) { } // don't let your confidence be shaken, just learn what you can from this young // master. QString Cat_start::string(Procinfo *p) { #ifdef SOLARIS if (p->state == 'Z') return "-"; // Solaris zombies have no valid start time #endif Proc *proc = p->proc; // time_t start = boot_time + p->starttime / (unsigned)HZ; QString s; // time_t start = proc->boot_time + p->starttime / proc->clk_tick; time_t start = p->starttime; char *ct = ctime(&start); // ctime(sec) time_t sec = p->starttime - proc->boot_time; // secs // if(p->tv.tv_sec - start < 86400) { if (sec < 86400) { // 24hours ct[16] = '\0'; s = ct + 11; // Hour:minu } else { ct[10] = '\0'; s = ct + 4; // Date } return s; } int Cat_start::compare(Procinfo *a, Procinfo *b) { unsigned long bs = b->starttime, as = a->starttime; return bs >= as ? (bs == as ? 0 : 1) : -1; } /* ======================== Procview ============================ */ float Procview::avg_factor = 1.0; Procview::Procview() { // Proc::Proc(); //call once more ? cats.clear(); reversed = false; viewproc = ALL; treeview = true; idxF_CMD = 0; // **** important **** viewfields = USER; set_fields(); sortcat = 0; // categories[F_PID]; sort_column = -1; sortcat_linear = NULL; enable = true; maxSizeHistory = 1200; refresh(); // before reading Watchdog list } // COMMON: // Description : // View mode : ALL , OWNER, NO-ROOOT , HIDDEN, NETWORK bool Procview::accept_proc(Procinfo *p) { QString pid; static int my_uid = getuid(); bool result; result = true; // BAD if (false and viewproc == NETWORK) { /* p->read_fds(); if(p->sock_inodes.size()==0) result=false; for(int i=0;isock_inodes.size();i++) { SockInode *sn=p->sock_inodes[i]; Sockinfo *si=Procinfo::socks.value(sn->inode,NULL); if(si) { si->pid=p->pid; linear_socks.append(si); } } */ } else if (viewproc == ALL) result = true; else if (viewproc == OWNED) result = (p->uid == my_uid); else if (viewproc == NROOT) result = (p->uid != 0); else if (viewproc == RUNNING) result = strchr("ORDW", p->state) != 0; /* if ( viewproc == HIDDEN) { result=false; for(int j=0;jcommand) result=true ; } else { for(int j=0;jcommand) result=false; } */ if (result == false) return false; // dont go further !! for better speed /// if(search_box==NULL) return result; if (filterstr == "") return result; if (filterstr == "*") return true; // Notice: // 1. some process name maybe different CMDLINE. ex) Xorg if (p->cmdline.contains(filterstr, Qt::CaseInsensitive)) // search_box->text() return true; if (p->command.contains(filterstr, Qt::CaseInsensitive)) return true; if (p->username.contains(filterstr, Qt::CaseInsensitive)) return true; pid = pid.setNum(p->pid); //=QString::number(p->pid); if (pid.contains(filterstr, Qt::CaseInsensitive)) return true; /// printf("search_Box =%s , %s /// \n",search_box->text().toAscii().data(),p->command.toAscii().data()); return false; } extern "C" { typedef int (*compare_func)(const void *, const void *); } /* template void Svec::sort(int (*compare)(const T *a, const T *b)) { qsort(vect, used, sizeof(T), (compare_func)compare); } */ // table view - sort void Procview::linearize_tree(QVector *ps, int level, int prow, bool hide) { static_sortcat = sortcat; // ps->sort(reversed ? compare_backwards : compare); if (reversed) qsort(ps->data(), ps->size(), sizeof(Procinfo *), (compare_func)compare_backwards); else qsort(ps->data(), ps->size(), sizeof(Procinfo *), (compare_func)compare); int size = ps->size(); // printf("level=%d prow=%d size=%d\n",level,prow,size); for (int i = 0; i < size; i++) { Procinfo *p = (*ps)[i]; // if (p->pid<6 ) printf("pid=%d level=%d // parent_row=%d\n",p->pid,p->pid,p->parent_row); p->level = level; p->lastchild = false; p->table_child_seq = i; // ************* where using ? -> sequence p->parent_row = prow; if (!hide) linear_procs.append(p); // need !! if (p->table_children.size()) // and !p->hidekids) linearize_tree(&p->table_children, level + 1, linear_procs.size() - 1, hide | p->hidekids); } if (size > 0) (*ps)[size - 1]->lastchild = true; } /// basic,memory,job fields void Procview::set_fields_list(int fields[]) { cats.clear(); for (int i = 0; fields[i] != F_END; i++) { if (fields[i] == F_CPUNUM and Proc::num_cpus < 2) continue; // not correctly work Category *c = categories.value(fields[i], NULL); if (c) { // printf("name=%s\n",c->name); cats.append(c); } } return; } // return the column number of a field, or -1 if not displayed int Procview::findCol(int field_id) { for (int i = 0; i < cats.size(); i++) if (cats[i]->id == field_id) return i; return -1; } // basis // called by // void Procview::fieldArrange(); // qps(); // void Procview::addField(int Fid, int where) { // where=-1; // printf("Fid =%d where=%d\n",Fid,where); // where=pstable->clickedColumn(); if (where == 0) { // if (pstable->treeMode()) where=1; } if (Fid == F_PROCESSNAME) where = 0; if (Fid == F_CMDLINE) where = cats.size(); // always should be the last column if (where < 0) { // this is default where = cats.size(); } if (where > cats.size()) // CMD_LINE ! ?????? where = 1; Category *newcat = categories[Fid]; // printf("name =%s where=%d\n",newcat->name,where); if (cats.indexOf(newcat) < 0) // if not in the list **** cats.insert(where, newcat); } // add a category to last by name void Procview::addField(char *name) // interface { // QString str=sl[i]; int id = field_id_by_name(name); if (id >= 0) addField(id); // add to last return; Category *cat = cat_by_name(name); if (cat) cats.append(cat); } void Procview::removeField(int field_id) { for (int i = 0; i < cats.size();) { // printf("Fid=%d cats[%d].id=%d //\n",field_id,i,cats[i]->id); if (true) { if (cats[i]->id == field_id) { // if(cats[i]->id==F_CMD and !treeview) // idxF_CMD=-1; cats.remove(i); break; } else i++; } } // printf("Fid=%d cats.size=%d\n",field_id,cats.size()); } // called by // 1. write_settings() // DEL? -> not yet void Procview::update_customfield() { int i; // printf("update custom_fields\n"); customfields.clear(); for (i = 0; i < cats.size(); i++) customfields.append(cats[i]->name); // DEL if (false and treeview) { int idx = customfields.indexOf("COMMAND"); // == removeField(F_CMD); if (idx >= 0) customfields.removeAt(idx); if (idxF_CMD >= 0) { /* if(customfields.size()<=idxF_CMD) customfields.append(categories[F_CMD]->name); else */ customfields.insert(idxF_CMD, categories[F_CMD]->name); } } return; } // Description : FIELD movement by mouse drag // From col To place void Procview::moveColumn(int col, int place) { int i; int f_size = cats.size(); if (col < 0 or place < 0 or f_size <= col or f_size <= place) { printf("QPS code bugs!! : moveColumn() col=%d place=%d " "cats.size=%d\n", col, place, cats.size()); return; } if (treeview == true) { // *** important : F_PROCESSNAME field should be the first in // TreeMode! /// if(place==0) return; if (cats[col]->index == F_PROCESSNAME) place = 0; } // COMMAND_LINE field should always be the last field // if(cats[place]->index==F_CMDLINE) return; if (cats[col]->index == F_CMDLINE) place = cats.size() - 1; Category *cat = cats[col]; // SEGFAULT POSSIBLE! cats.insert(place, cat); // insert if (place < col) col++; cats.remove(col); // remove idx // refresh(); /// update_customfield(); } // always called when linear to tree // DEL void Procview::saveCOMMANDFIELD() {} // call by // void Pstable::moveCol(int col, int place) // void Pstable::setTreeMode(bool treemode) // void Procview::set_fields() // TODO: checkField(); void Procview::fieldArrange() { if (treeview == true) { // Tree Mode // If ProcessName isn't the leftmost column, move it to // leftmost // *** important : F_PROCESSNAME field should be the first in // TreeMode! if (cats[0]->index != F_PROCESSNAME) { // find F_PROCESSNAME for (int i = 1; i < cats.size(); i++) { if (cats[i]->index == F_PROCESSNAME) moveColumn(i, 0); } } // PID sort for convenience (default) /* if(false and cats[i]->index == F_PID ) { reversed = false; sortcat = cats[i]; //// pstable->setSortedCol(i); } */ // Linear_Mode: } if (true) { for (int i = 0; i < cats.size(); i++) { if (cats[i]->index == F_CMDLINE) { // COMMAND_LINE field should always be the last // field if (i == (cats.size() - 1)) moveColumn(i, 0); else { } } } } } void Procview::setTreeMode(bool b) { if (treeview == false) { if (sortcat_linear == NULL) sortcat_linear = sortcat; else sortcat = sortcat_linear; } } void Procview::setSortColumn(int col, bool r) { // qDebug("xxx error? col>=cats.size() %d",col); if (col >= cats.size()) { qDebug("hmmm error? col>=cats.size() %d", col); return; } // if(!procview->treeview) just reverse the lines if (col == sort_column) reversed = !reversed; else reversed = false; sortcat = cats[col]; sort_column = col; } Category *Procview::static_sortcat = 0; int Procview::compare(Procinfo *const *a, Procinfo *const *b) { if (static_sortcat == 0) return 0; int r = static_sortcat->compare(*a, *b); return (r == 0) ? ((*a)->pid > (*b)->pid ? 1 : -1) : r; } int Procview::compare_backwards(Procinfo *const *a, Procinfo *const *b) { if (static_sortcat == 0) return 0; int r = static_sortcat->compare(*b, *a); return (r == 0) ? ((*b)->pid > (*a)->pid ? 1 : -1) : r; } // COMMON Category *Proc::cat_by_name(const char *s) { if (s) { // java style QHashIterator i(categories); while (i.hasNext()) { const char *p; i.next(); p = i.value()->name; if (*p == ' ') p++; if (strcmp(p, s) == 0) return i.value(); // cout << i.key() << ": " << i.value() << endl; } } return 0; } // COMMON // call by int Proc::field_id_by_name(const char *s) { if (s) { // STL style QHash::iterator i = categories.begin(); while (i != categories.end()) { if (strcmp(i.value()->name, s) == 0) return i.key(); // cout << i.key() << ": " << // i.value() << endl; ++i; } } return -1; } // postInit void Proc::commonPostInit() { // java style /* QHashIterator i(categories); // a little suck! while (i.hasNext()) { i.next(); i.value()->index=i.key(); i.value()->id=i.key(); }*/ // STL style , set id QHash::iterator i = categories.begin(); while (i != categories.end()) { i.value()->index = i.key(); i.value()->id = i.key(); // cout << i.key() << ": " << i.value() << endl; ++i; } num_opened_files = 0; // test num_process = 0; // 64bit num_network_process = 0; // 64bit dt_total = 0; // diff system tick dt_used = 0; // for infobar loadavg[0] = loadavg[1] = loadavg[2] = 0.0; // CPU load avg 1min,5min,15minute Proc::num_cpus = 0; Proc::old_num_cpus = 0; Proc::mem_total = 0; Proc::mem_free = 0; Proc::mem_buffers = 0; Proc::mem_cached = 0; // mem_shared = 0; // only linux kernel 2.4.x??? Proc::swap_total = 0; Proc::swap_free = 0; Proc::qps_pid = -1; Proc::loadQps = 0.0; Proc::cpu_times_vec = 0; // array. Proc::old_cpu_times_vec = 0; Proc::boot_time = 0; Proc::clk_tick = 100; // for most system current_gen = 0; // ! mprocs = NULL; hprocs = NULL; clk_tick = sysconf(_SC_CLK_TCK); //****** The number of clock ticks per second. // // printf("Qps: hz=%d\n",clk_tick); /// Procinfo::init_static(); } // COMMON? // called by Procview::rebuild() // using procs, makes root_procs for TABLE_View void Procview::build_tree(Proclist &procs) { Procinfo *p; int proc_n = 0; // children clear root_procs.clear(); QHash::iterator i; for (i = procs.begin(); i != procs.end();) { Procinfo *p = i.value(); p->table_children.clear(); if ((p->accepted = accept_proc(p))) proc_n++; ++i; } Proc::num_process = proc_n; // count process // find parent of a process for (i = procs.begin(); i != procs.end(); ++i) { p = i.value(); // always not NULL if (p->accepted) { Procinfo *parent = 0; int virtual_parent_pid; if (p->isThread()) virtual_parent_pid = p->tgid; // thread's leader ID. else virtual_parent_pid = p->ppid; if (p->pid < p->ppid) { // this occurs !! a reporter mailed to me } parent = procs.value(virtual_parent_pid, NULL); // if pid not found, then return NULL. // printf("thread_leader=0 (%d) //%s\n",p->tgid,p->command.ascii()); if (treeview and parent and parent->accepted) { // p->table_child_seq=parent->table_children.size(); parent->table_children.append(p); } else { // 1.init(pid=1) process // 2.some process which parent not accepted // 3.(null) thread has TGID=0,PPID=0 // 4.when not tree mode root_procs.append(p); } } } } // COMMON // BOTTLENECK No.3 0.5% // re-sort the process list , ::rebuild() for Table // called by Pstable::refresh() void Procview::rebuild() { linear_procs.clear(); // Svec procs in Proview #ifdef LINUX linear_socks.clear(); #endif // Procinfo *pi = getProcinfoByPID(Procinfo::qps_pid); // if(pi) Procinfo::loadQps=pi->pcpu; // printf("rebuild\n"); /* if(mprocs) { build_tree(*mprocs); } else */ build_tree(Proc::procs); linearize_tree(&root_procs, 0, -1); } #include // log() // COMMON: CORE // Description: update the process list BottleNeck 1.5% // read /proc/* // called by Procview::refresh(), every UPDATE . void Proc::refresh() { current_gen++; // TEST for Process History SysHistory *s = new SysHistory; history.append(s); hprocs = &(s->procs); // init /// num_opened_files=0; Proc::num_process = 0; Proc::num_network_process = 0; Proc::read_loadavg(); Proc::read_system(); // **** should be every refresh !! // s->load_cpu=(float)Proc::dt_used/Proc::dt_total; //after // read_system(); s->load_cpu = load_cpu; // after read_system(); s->time = time(NULL); // save current time in seconds since epoch // TODO: clean // Procinfo::read_sockets(); // for future, BottleNect 2% read_byte = 0; write_byte = 0; io_byte = 0; //!!! /// Proc::read_sockets(); // test read_proc_all(); // Linux, Solaris... // s->load_io=(float)(read_byte + write_byte ); // (50*1024*1024); // //dt_total; if (io_byte != 0) { s->load_io = log10(io_byte) * 2; // 9000 -> 3point // printf("DEBUG: rw=[%d,%d] %d, %f\n",read_byte, // write_byte,io_byte,s->load_io); } else s->load_io = 0; // remove non-existing processes, remove Procinfos of nonexisting // processes QHash::iterator i; for (i = procs.begin(); i != procs.end();) { Procinfo *p = i.value(); if (p->generation != current_gen) { // printf("delete %d\n",p->pid); i = procs.erase(i); delete p; } else { // p->table_children.clear(); // for rebuild() // p->accepted=accept_proc(p); ++i; } } // TESTING while (history.size() >= maxSizeHistory) { // if(history.isEmpty()==false) delete history.takeFirst(); } } // COMMON // read new process info void Procview::refresh() { /****************************************************************/ /* Procview.procs has the procinfo */ /****************************************************************/ if (enable) { // printf("Procview::refresh()\n"); Proc::refresh(); // read "/proc/*", then update the process list } } SysHistory::~SysHistory() { Procinfo *p; foreach (p, procs) { delete p; } procs.clear(); // remove all (key,val) } /* check Category *Proc::cat_by_name(const char *s) { if( s ) { for( int i = 0; i < categories.size(); i++ ) if( strcmp(categories[i]->name, s) == 0 ) return categories[i]; } return 0; } int Proc::field_id_by_name(const char *s) { if( s ) { for( int i = 0; i < categories.size(); i++ ) if( strcmp(categories[i]->name, s) == 0 ) return i; } return -1; } */