/* * Copyright (c) 2014, Red Hat. * Copyright (c) 2006, Ken McDonell. All Rights Reserved. * Copyright (c) 2007-2009, Aconex. All Rights Reserved. * * 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. */ #include #include #include #include #include "main.h" #include "openviewdialog.h" #define DESPERATE 0 int Cflag; int Hflag; int Lflag; int Wflag; char *outfile; char *outgeometry; QFont *globalFont; Settings globalSettings; // Globals used to provide single instances of classes used across pmchart GroupControl *liveGroup; // one metrics class group for all hosts GroupControl *archiveGroup; // one metrics class group for all archives GroupControl *activeGroup; // currently active metric fetchgroup TimeControl *pmtime; // one timecontrol class for pmtime PmChart *pmchart; static pmLongOptions longopts[] = { PMAPI_OPTIONS_HEADER("General options"), PMOPT_ALIGN, PMOPT_ARCHIVE, PMOPT_DEBUG, PMOPT_HOST, PMOPT_HOSTSFILE, PMOPT_NAMESPACE, PMOPT_SPECLOCAL, PMOPT_LOCALPMDA, PMOPT_ORIGIN, PMOPT_GUIPORT, PMOPT_START, PMOPT_SAMPLES, PMOPT_FINISH, PMOPT_INTERVAL, PMOPT_TIMEZONE, PMOPT_HOSTZONE, PMOPT_VERSION, PMOPT_HELP, PMAPI_OPTIONS_HEADER("Display options"), { "view", 1, 'c', "VIEW", "chart view(s) to load on startup" }, { "check", 0, 'C', 0, "parse views, report any errors and exit" }, { "font-size", 1, 'F', "SIZE", "use font of given size" }, { "font-family", 1, 'f', "FONT", "use font family" }, { "geometry", 1, 'g', "WxH", "image geometry Width x Height" }, { "output", 1, 'o', "FILE", "export image to FILE (type from suffix)" }, { "samples", 1, 's', "N", "buffer up N points of sample history" }, { "visible", 1, 'v', "N", "display N points of visible history" }, { "white", 0, 'W', 0, "export images using an opaque (white) background" }, PMAPI_OPTIONS_END }; // a := a + b for struct timevals void tadd(struct timeval *a, struct timeval *b) { a->tv_usec += b->tv_usec; if (a->tv_usec > 1000000) { a->tv_usec -= 1000000; a->tv_sec++; } a->tv_sec += b->tv_sec; } // // a : b for struct timevals ... <0 for a0 for a>b // int tcmp(struct timeval *a, struct timeval *b) { int res = (int)(a->tv_sec - b->tv_sec); if (res == 0) res = (int)(a->tv_usec - b->tv_usec); return res; } // convert timeval to seconds double tosec(struct timeval t) { return t.tv_sec + (t.tv_usec / 1000000.0); } // create a time range in seconds from (delta x points) double torange(struct timeval t, int points) { return tosec(t) * points; } // conversion from seconds (double precision) to struct timeval void fromsec(double value, struct timeval *tv) { tv->tv_sec = (time_t)value; tv->tv_usec = (long)(((value - (double)tv->tv_sec) * 1000000.0)); } // debugging, display seconds-since-epoch in human readable format char *timeString(double seconds) { static char string[32]; time_t secs = (time_t)seconds; char *s; s = pmCtime(&secs, string); s[strlen(s)-1] = '\0'; return s; } // return a string containing hour and milliseconds char *timeHiResString(double time) { static char s[16]; char m[8]; time_t secs = (time_t)time; struct tm t; sprintf(m, "%.3f", time - floor(time)); pmLocaltime(&secs, &t); sprintf(s, "%02d:%02d:%02d.%s", t.tm_hour, t.tm_min, t.tm_sec, m+2); s[strlen(s)-1] = '\0'; return s; } void nomem(void) { // no point trying to report anything ... dump core is the best bet abort(); } void setupEnvironment(void) { char *value; QString confirm = pmGetConfig("PCP_BIN_DIR"); confirm.prepend("PCP_XCONFIRM_PROG="); confirm.append(QChar(__pmPathSeparator())); confirm.append("pmquery"); if ((value = strdup((const char *)confirm.toAscii())) != NULL) putenv(value); if (getenv("PCP_STDERR") == NULL && // do not overwrite, for QA ((value = strdup("PCP_STDERR=DISPLAY")) != NULL)) putenv(value); QCoreApplication::setOrganizationName("PCP"); QCoreApplication::setApplicationName(pmProgname); } void writeSettings(void) { QSettings userSettings; userSettings.beginGroup(pmProgname); if (globalSettings.chartDeltaModified) { globalSettings.chartDeltaModified = false; userSettings.setValue("chartDelta", globalSettings.chartDelta); } if (globalSettings.loggerDeltaModified) { globalSettings.loggerDeltaModified = false; userSettings.setValue("loggerDelta", globalSettings.loggerDelta); } if (globalSettings.sampleHistoryModified) { globalSettings.sampleHistoryModified = false; userSettings.setValue("sampleHistory", globalSettings.sampleHistory); } if (globalSettings.visibleHistoryModified) { globalSettings.visibleHistoryModified = false; userSettings.setValue("visibleHistory", globalSettings.visibleHistory); } if (globalSettings.defaultSchemeModified) { globalSettings.defaultSchemeModified = false; userSettings.setValue("defaultColorScheme", globalSettings.defaultScheme.colorNames()); } if (globalSettings.colorSchemesModified) { globalSettings.colorSchemesModified = false; userSettings.beginWriteArray("schemes"); for (int i = 0; i < globalSettings.colorSchemes.size(); i++) { userSettings.setArrayIndex(i); userSettings.setValue("name", globalSettings.colorSchemes[i].name()); userSettings.setValue("colors", globalSettings.colorSchemes[i].colorNames()); } userSettings.endArray(); } if (globalSettings.chartBackgroundModified) { globalSettings.chartBackgroundModified = false; userSettings.setValue("chartBackgroundColor", globalSettings.chartBackgroundName); } if (globalSettings.chartHighlightModified) { globalSettings.chartHighlightModified = false; userSettings.setValue("chartHighlightColor", globalSettings.chartHighlightName); } if (globalSettings.initialToolbarModified) { globalSettings.initialToolbarModified = false; userSettings.setValue("initialToolbar", globalSettings.initialToolbar); } if (globalSettings.nativeToolbarModified) { globalSettings.nativeToolbarModified = false; userSettings.setValue("nativeToolbar", globalSettings.nativeToolbar); } if (globalSettings.toolbarLocationModified) { globalSettings.toolbarLocationModified = false; userSettings.setValue("toolbarLocation", globalSettings.toolbarLocation); } if (globalSettings.toolbarActionsModified) { globalSettings.toolbarActionsModified = false; userSettings.setValue("toolbarActions", globalSettings.toolbarActions); } if (globalSettings.fontFamilyModified) { globalSettings.fontFamilyModified = false; if (globalSettings.fontFamily != QString(PmChart::defaultFontFamily())) userSettings.setValue("fontFamily", globalSettings.fontFamily); else userSettings.remove("fontFamily"); } if (globalSettings.fontStyleModified) { globalSettings.fontStyleModified = false; if (globalSettings.fontStyle != QString("Normal")) userSettings.setValue("fontStyle", globalSettings.fontStyle); else userSettings.remove("fontStyle"); } if (globalSettings.fontSizeModified) { globalSettings.fontSizeModified = false; if (globalSettings.fontSize != PmChart::defaultFontSize()) userSettings.setValue("fontSize", globalSettings.fontSize); else userSettings.remove("fontSize"); } if (globalSettings.savedHostsModified) { globalSettings.savedHostsModified = false; if (globalSettings.savedHosts.isEmpty() == false) userSettings.setValue("savedHosts", globalSettings.savedHosts); else userSettings.remove("savedHosts"); } userSettings.endGroup(); } void checkHistory(int samples, int visible) { // sanity checking on sample sizes if (samples < PmChart::minimumPoints()) { globalSettings.sampleHistory = PmChart::minimumPoints(); globalSettings.sampleHistoryModified = true; } if (samples > PmChart::maximumPoints()) { globalSettings.sampleHistory = PmChart::maximumPoints(); globalSettings.sampleHistoryModified = true; } if (visible < PmChart::minimumPoints()) { globalSettings.visibleHistory = PmChart::minimumPoints(); globalSettings.visibleHistoryModified = true; } if (visible > PmChart::maximumPoints()) { globalSettings.visibleHistory = PmChart::maximumPoints(); globalSettings.visibleHistoryModified = true; } if (samples < visible) { globalSettings.sampleHistory = globalSettings.visibleHistory; globalSettings.sampleHistoryModified = true; } } static void readSettings(void) { QSettings userSettings; userSettings.beginGroup(pmProgname); // // Parameters related to sampling // globalSettings.chartDeltaModified = false; globalSettings.chartDelta = userSettings.value("chartDelta", PmChart::defaultChartDelta()).toDouble(); globalSettings.loggerDeltaModified = false; globalSettings.loggerDelta = userSettings.value("loggerDelta", PmChart::defaultLoggerDelta()).toDouble(); globalSettings.sampleHistoryModified = false; globalSettings.sampleHistory = userSettings.value("sampleHistory", PmChart::defaultSampleHistory()).toInt(); globalSettings.visibleHistoryModified = false; globalSettings.visibleHistory = userSettings.value("visibleHistory", PmChart::defaultVisibleHistory()).toInt(); checkHistory(globalSettings.sampleHistory, globalSettings.visibleHistory); if (globalSettings.sampleHistoryModified) { userSettings.setValue("samplePoints", globalSettings.sampleHistory); globalSettings.sampleHistoryModified = false; } if (globalSettings.visibleHistoryModified) { userSettings.setValue("visiblePoints", globalSettings.visibleHistory); globalSettings.visibleHistoryModified = false; } // // Everything colour (scheme) related // QStringList colorList; globalSettings.defaultSchemeModified = false; if (userSettings.contains("defaultColorScheme") == true) colorList = userSettings.value("defaultColorScheme").toStringList(); else colorList << "#ffff00" << "#0000ff" << "#ff0000" << "#008000" << "#ee82ee" << "#aa5500" << "#666666" << "#aaff00" << "#aa00ff" << "#aaaa7f"; globalSettings.defaultScheme.setName("#-cycle"); globalSettings.defaultScheme.setModified(false); globalSettings.defaultScheme.setColorNames(colorList); int size = userSettings.beginReadArray("schemes"); for (int i = 0; i < size; i++) { userSettings.setArrayIndex(i); ColorScheme scheme; scheme.setName(userSettings.value("name").toString()); scheme.setModified(false); scheme.setColorNames(userSettings.value("colors").toStringList()); globalSettings.colorSchemes.append(scheme); } userSettings.endArray(); // // Everything (else) colour related // globalSettings.chartBackgroundModified = false; globalSettings.chartBackgroundName = userSettings.value( "chartBackgroundColor", "#6ca2c9").toString(); globalSettings.chartBackground = QColor(globalSettings.chartBackgroundName); globalSettings.chartHighlightModified = false; globalSettings.chartHighlightName = userSettings.value( "chartHighlightColor", "blue").toString(); globalSettings.chartHighlight = QColor(globalSettings.chartHighlightName); // // Toolbar user preferences // globalSettings.initialToolbarModified = false; globalSettings.initialToolbar = userSettings.value( "initialToolbar", 1).toInt(); globalSettings.nativeToolbarModified = false; globalSettings.nativeToolbar = userSettings.value( "nativeToolbar", 1).toInt(); globalSettings.toolbarLocationModified = false; globalSettings.toolbarLocation = userSettings.value( "toolbarLocation", 0).toInt(); QStringList actionList; globalSettings.toolbarActionsModified = false; if (userSettings.contains("toolbarActions") == true) globalSettings.toolbarActions = userSettings.value("toolbarActions").toStringList(); // else: (defaults come from the pmchart.ui interface specification) // // Font preferences // globalSettings.fontFamilyModified = false; globalSettings.fontFamily = userSettings.value( "fontFamily", PmChart::defaultFontFamily()).toString(); globalSettings.fontStyleModified = false; QString fontStyle; globalSettings.fontStyle = userSettings.value( "fontStyle", "Normal").toString(); globalSettings.fontSizeModified = false; globalSettings.fontSize = userSettings.value( "fontSize", PmChart::defaultFontSize()).toInt(); // // Saved Hosts list preferences // globalSettings.savedHostsModified = false; if (userSettings.contains("savedHosts") == true) globalSettings.savedHosts = userSettings.value("savedHosts").toStringList(); userSettings.endGroup(); } static void readSchemes(void) { QChar sep(__pmPathSeparator()); QString schemes = pmGetConfig("PCP_VAR_DIR"); schemes.append(sep).append("config"); schemes.append(sep).append("pmchart"); schemes.append(sep).append("Schemes"); QFileInfo fi(schemes); if (fi.exists()) OpenViewDialog::openView(schemes.toAscii()); } // Get next color from given scheme or from default colors for #-cycle QColor nextColor(QString scheme, int *sequence) { QList colorList; int seq = (*sequence)++; for (int i = 0; i < globalSettings.colorSchemes.size(); i++) { if (globalSettings.colorSchemes[i].name() == scheme) { colorList = globalSettings.colorSchemes[i].colors(); break; } } if (colorList.size() < 2) // common case colorList = globalSettings.defaultScheme.colors(); if (colorList.size() < 2) // idiot user!? colorList << QColor("yellow") << QColor("blue") << QColor("red") << QColor("green") << QColor("violet"); seq %= colorList.size(); return colorList.at(seq); } static void setupViewGlobals() { int w, h, points, x, y; OpenViewDialog::globals(&w, &h, &points, &x, &y); if (w || h) { QSize size = pmchart->size().expandedTo(QSize(w, h)); QSize desk = QApplication::desktop()->availableGeometry().size(); pmchart->resize(size.boundedTo(desk)); } if (x || y) { QPoint pos = pmchart->pos(); if (x) pos.setX(x); if (y) pos.setY(y); pmchart->move(pos); } if (points) { if (activeGroup->sampleHistory() < points) activeGroup->setSampleHistory(points); activeGroup->setVisibleHistory(points); } } static int override(int opt, pmOptions *opts) { (void)opts; if (opt == 'g') return 1; if (opt == 'H') Hflag = 1; if (opt == 'L') Lflag = 1; return 0; } int main(int argc, char ** argv) { int c, sts; int sh = -1; /* sample history length */ int vh = -1; /* visible history length */ char *endnum; Tab *tab; struct timeval logStartTime; struct timeval logEndTime; QStringList configs; QString tzLabel; QString tzString; pmOptions opts; memset(&opts, 0, sizeof(opts)); __pmtimevalNow(&opts.origin); __pmSetProgname(argv[0]); QApplication a(argc, argv); setupEnvironment(); readSettings(); opts.flags = PM_OPTFLAG_MULTI | PM_OPTFLAG_MIXED; opts.short_options = "A:a:Cc:D:f:F:g:h:H:Ln:o:O:p:s:S:T:t:Vv:WzZ:?"; opts.long_options = longopts; opts.short_usage = "[options] [sources]"; opts.override = override; while ((c = pmGetOptions(argc, argv, &opts)) != EOF) { switch (c) { case 'C': Cflag++; break; case 'c': configs.append(opts.optarg); break; case 'f': globalSettings.fontFamily = opts.optarg; break; case 'F': sts = (int)strtol(opts.optarg, &endnum, 10); if (*endnum != '\0' || c < 0) { pmprintf("%s: -F requires a numeric argument\n", pmProgname); opts.errors++; } else { globalSettings.fontSize = sts; } break; case 'g': outgeometry = opts.optarg; break; case 'o': /* output image file */ outfile = opts.optarg; break; case 'W': /* white image background */ Wflag = 1; break; case 'v': /* visible history */ vh = (int)strtol(opts.optarg, &endnum, 10); if (*endnum != '\0' || vh < 1) { pmprintf("%s: -v requires a numeric argument, larger than 1\n", pmProgname); opts.errors++; } break; } } /* hosts from a Hosts file are added to the SavedHosts list */ if (Hflag) { for (int i = 0; i < opts.nhosts; i++) globalSettings.savedHosts.append(opts.hosts[i]); globalSettings.savedHostsModified = true; } if (opts.narchives > 0) { while (opts.optind < argc) __pmAddOptArchive(&opts, argv[opts.optind++]); } else { if (!Hflag) { for (c = 0; c < globalSettings.savedHosts.size(); c++) { const QString &host = globalSettings.savedHosts.at(c); __pmAddOptHost(&opts, (char *)(const char *)host.toAscii()); } } while (opts.optind < argc) __pmAddOptHost(&opts, argv[opts.optind++]); } if (opts.optind != argc) opts.errors++; if (opts.errors) { pmUsageMessage(&opts); exit(1); } /* set initial sampling interval from command line, else global setting */ if (opts.interval.tv_sec == 0 && opts.interval.tv_usec == 0) fromsec(globalSettings.chartDelta, &opts.interval); console = new QedConsole(opts.origin); // // Deal with user requested sample/visible points globalSettings. These // (command line) override the QSettings values, for this instance // of pmchart. They should not be written though, unless requested // later via the Settings dialog. // sh = opts.samples ? opts.samples : -1; if (vh != -1 || sh != -1) { if (sh == -1) sh = globalSettings.sampleHistory; if (vh == -1) vh = globalSettings.visibleHistory; checkHistory(sh, vh); if (globalSettings.sampleHistoryModified || globalSettings.visibleHistoryModified) { pmprintf("%s: invalid sample/visible history\n", pmProgname); pmflush(); exit(1); } globalSettings.sampleHistory = sh; globalSettings.visibleHistory = vh; } console->post("Global settings setup complete"); // Create all of the sources liveGroup = new GroupControl(); archiveGroup = new GroupControl(); if (Lflag) liveGroup->use(PM_CONTEXT_LOCAL, QmcSource::localHost); sts = opts.nhosts + opts.narchives; for (c = 0; c < opts.nhosts; c++) if (liveGroup->use(PM_CONTEXT_HOST, opts.hosts[c]) < 0) sts--; for (c = 0; c < opts.narchives; c++) if (archiveGroup->use(PM_CONTEXT_ARCHIVE, opts.archives[c]) < 0) sts--; if (Lflag == 0 && sts == 0) liveGroup->createLocalContext(); pmflush(); console->post("Metric group setup complete (%d hosts, %d archives)", opts.nhosts, opts.narchives); if (opts.tzflag) { if (opts.narchives > 0) archiveGroup->useTZ(); if (opts.nhosts > 0) liveGroup->useTZ(); } else if (opts.timezone != NULL) { if (opts.narchives > 0) archiveGroup->useTZ(QString(opts.timezone)); if (opts.nhosts > 0) liveGroup->useTZ(QString(opts.timezone)); if ((sts = pmNewZone(opts.timezone)) < 0) { pmprintf("%s: cannot set timezone to \"%s\": %s\n", pmProgname, (char *)opts.timezone, pmErrStr(sts)); pmflush(); exit(1); } } // // Choose which Tab will be displayed initially - archive/live. // If any archives given on command line, we go Archive mode; // otherwise Live mode wins. Our initial pmtime connection is // set in that mode too. Later we'll make a second connection // in the other mode (and only "on-demand"). // if (opts.narchives > 0) { archiveGroup->defaultTZ(tzLabel, tzString); archiveGroup->updateBounds(); logStartTime = archiveGroup->logStart(); logEndTime = archiveGroup->logEnd(); if ((sts = pmParseTimeWindow(opts.start_optarg, opts.finish_optarg, opts.align_optarg, opts.origin_optarg, &logStartTime, &logEndTime, &opts.start, &opts.finish, &opts.origin, &endnum)) < 0) { pmprintf("Cannot parse archive time window\n%s\n", endnum); pmUsageMessage(&opts); free(endnum); exit(1); } // move position to account for initial visible points if (tcmp(&opts.origin, &opts.start) <= 0) for (c = 0; c < globalSettings.visibleHistory - 2; c++) tadd(&opts.origin, &opts.interval); if (tcmp(&opts.origin, &opts.finish) > 0) opts.origin = opts.finish; } else { liveGroup->defaultTZ(tzLabel, tzString); __pmtimevalNow(&logStartTime); logEndTime.tv_sec = logEndTime.tv_usec = INT_MAX; if ((sts = pmParseTimeWindow(opts.start_optarg, opts.finish_optarg, opts.align_optarg, opts.origin_optarg, &logStartTime, &logEndTime, &opts.start, &opts.finish, &opts.origin, &endnum)) < 0) { pmprintf("Cannot parse live time window\n%s\n", endnum); pmUsageMessage(&opts); free(endnum); exit(1); } } console->post("Timezones and time window setup complete"); globalFont = new QFont(globalSettings.fontFamily, globalSettings.fontSize); if (globalSettings.fontStyle.contains("Italic")) globalFont->setItalic(true); if (globalSettings.fontStyle.contains("Bold")) globalFont->setBold(true); tab = new Tab; fileIconProvider = new QedFileIconProvider(); pmchart = new PmChart; pmtime = new TimeControl; console->post("Phase1 user interface constructors complete"); // Start pmtime process for time management pmtime->init(opts.guiport, opts.narchives == 0, &opts.interval, &opts.origin, &opts.start, &opts.finish, tzString, tzLabel); pmchart->init(); liveGroup->init(globalSettings.sampleHistory, globalSettings.visibleHistory, pmtime->liveInterval(), pmtime->livePosition()); archiveGroup->init(globalSettings.sampleHistory, globalSettings.visibleHistory, pmtime->archiveInterval(), pmtime->archivePosition()); // // We setup the pmchart tab list late, so we don't have to deal // with pmtime messages reaching the Tabs until we're all setup. // if (opts.narchives == 0) tab->init(pmchart->tabWidget(), liveGroup, "Live"); else tab->init(pmchart->tabWidget(), archiveGroup, "Archive"); pmchart->tabWidget()->insertTab(tab); pmchart->setActiveTab(0, true); console->post("Phase2 user interface setup complete"); readSchemes(); for (c = 0; c < configs.size(); c++) if (!OpenViewDialog::openView((const char *)configs[c].toAscii())) opts.errors++; if (opts.errors) exit(1); setupViewGlobals(); pmflush(); if (Cflag) // done with -c config, quit return 0; pmchart->enableUi(); pmchart->show(); console->post("Top level window shown"); a.connect(&a, SIGNAL(lastWindowClosed()), pmchart, SLOT(quit())); return a.exec(); }