2 * Copyright (c) 2014, Red Hat.
3 * Copyright (c) 2006, Ken McDonell. All Rights Reserved.
4 * Copyright (c) 2007-2009, Aconex. All Rights Reserved.
6 * This program is free software; you can redistribute it and/or modify it
7 * under the terms of the GNU General Public License as published by the
8 * Free Software Foundation; either version 2 of the License, or (at your
9 * option) any later version.
11 * This program is distributed in the hope that it will be useful, but
12 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
13 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
18 #include <QApplication>
19 #include <QDesktopWidget>
21 #include "openviewdialog.h"
32 Settings globalSettings
;
34 // Globals used to provide single instances of classes used across pmchart
35 GroupControl
*liveGroup
; // one metrics class group for all hosts
36 GroupControl
*archiveGroup
; // one metrics class group for all archives
37 GroupControl
*activeGroup
; // currently active metric fetchgroup
38 TimeControl
*pmtime
; // one timecontrol class for pmtime
41 static pmLongOptions longopts
[] = {
42 PMAPI_OPTIONS_HEADER("General options"),
60 PMAPI_OPTIONS_HEADER("Display options"),
61 { "view", 1, 'c', "VIEW", "chart view(s) to load on startup" },
62 { "check", 0, 'C', 0, "parse views, report any errors and exit" },
63 { "font-size", 1, 'F', "SIZE", "use font of given size" },
64 { "font-family", 1, 'f', "FONT", "use font family" },
65 { "geometry", 1, 'g', "WxH", "image geometry Width x Height" },
66 { "output", 1, 'o', "FILE", "export image to FILE (type from suffix)" },
67 { "samples", 1, 's', "N", "buffer up N points of sample history" },
68 { "visible", 1, 'v', "N", "display N points of visible history" },
69 { "white", 0, 'W', 0, "export images using an opaque (white) background" },
74 // a : b for struct timevals ... <0 for a<b, ==0 for a==b, >0 for a>b
76 int tcmp(struct timeval
*a
, struct timeval
*b
)
78 int res
= (int)(a
->tv_sec
- b
->tv_sec
);
80 res
= (int)(a
->tv_usec
- b
->tv_usec
);
84 // create a time range in seconds from (delta x points)
85 double torange(struct timeval t
, int points
)
87 return __pmtimevalToReal(&t
) * points
;
90 // debugging, display seconds-since-epoch in human readable format
91 char *timeString(double seconds
)
93 static char string
[32];
94 time_t secs
= (time_t)seconds
;
97 s
= pmCtime(&secs
, string
);
98 s
[strlen(s
)-1] = '\0';
102 // return a string containing hour and milliseconds
103 char *timeHiResString(double time
)
107 time_t secs
= (time_t)time
;
110 sprintf(m
, "%.3f", time
- floor(time
));
111 pmLocaltime(&secs
, &t
);
112 sprintf(s
, "%02d:%02d:%02d.%s", t
.tm_hour
, t
.tm_min
, t
.tm_sec
, m
+2);
113 s
[strlen(s
)-1] = '\0';
119 // no point trying to report anything ... dump core is the best bet
123 void setupEnvironment(void)
126 QString confirm
= pmGetConfig("PCP_BIN_DIR");
127 confirm
.prepend("PCP_XCONFIRM_PROG=");
128 confirm
.append(QChar(__pmPathSeparator()));
129 confirm
.append("pmquery");
130 if ((value
= strdup((const char *)confirm
.toLatin1())) != NULL
)
132 if (getenv("PCP_STDERR") == NULL
&& // do not overwrite, for QA
133 ((value
= strdup("PCP_STDERR=DISPLAY")) != NULL
))
136 QCoreApplication::setOrganizationName("PCP");
137 QCoreApplication::setApplicationName(pmProgname
);
140 void writeSettings(void)
142 QSettings userSettings
;
144 userSettings
.beginGroup(pmProgname
);
145 if (globalSettings
.chartDeltaModified
) {
146 globalSettings
.chartDeltaModified
= false;
147 userSettings
.setValue("chartDelta", globalSettings
.chartDelta
);
149 if (globalSettings
.loggerDeltaModified
) {
150 globalSettings
.loggerDeltaModified
= false;
151 userSettings
.setValue("loggerDelta", globalSettings
.loggerDelta
);
153 if (globalSettings
.sampleHistoryModified
) {
154 globalSettings
.sampleHistoryModified
= false;
155 userSettings
.setValue("sampleHistory", globalSettings
.sampleHistory
);
157 if (globalSettings
.visibleHistoryModified
) {
158 globalSettings
.visibleHistoryModified
= false;
159 userSettings
.setValue("visibleHistory", globalSettings
.visibleHistory
);
161 if (globalSettings
.defaultSchemeModified
) {
162 globalSettings
.defaultSchemeModified
= false;
163 userSettings
.setValue("defaultColorScheme",
164 globalSettings
.defaultScheme
.colorNames());
166 if (globalSettings
.colorSchemesModified
) {
167 globalSettings
.colorSchemesModified
= false;
168 userSettings
.beginWriteArray("schemes");
169 for (int i
= 0; i
< globalSettings
.colorSchemes
.size(); i
++) {
170 userSettings
.setArrayIndex(i
);
171 userSettings
.setValue("name",
172 globalSettings
.colorSchemes
[i
].name());
173 userSettings
.setValue("colors",
174 globalSettings
.colorSchemes
[i
].colorNames());
176 userSettings
.endArray();
178 if (globalSettings
.chartBackgroundModified
) {
179 globalSettings
.chartBackgroundModified
= false;
180 userSettings
.setValue("chartBackgroundColor",
181 globalSettings
.chartBackgroundName
);
183 if (globalSettings
.chartHighlightModified
) {
184 globalSettings
.chartHighlightModified
= false;
185 userSettings
.setValue("chartHighlightColor",
186 globalSettings
.chartHighlightName
);
188 if (globalSettings
.initialToolbarModified
) {
189 globalSettings
.initialToolbarModified
= false;
190 userSettings
.setValue("initialToolbar", globalSettings
.initialToolbar
);
192 if (globalSettings
.nativeToolbarModified
) {
193 globalSettings
.nativeToolbarModified
= false;
194 userSettings
.setValue("nativeToolbar", globalSettings
.nativeToolbar
);
196 if (globalSettings
.toolbarLocationModified
) {
197 globalSettings
.toolbarLocationModified
= false;
198 userSettings
.setValue("toolbarLocation",
199 globalSettings
.toolbarLocation
);
201 if (globalSettings
.toolbarActionsModified
) {
202 globalSettings
.toolbarActionsModified
= false;
203 userSettings
.setValue("toolbarActions", globalSettings
.toolbarActions
);
205 if (globalSettings
.fontFamilyModified
) {
206 globalSettings
.fontFamilyModified
= false;
207 if (globalSettings
.fontFamily
!= QString(PmChart::defaultFontFamily()))
208 userSettings
.setValue("fontFamily", globalSettings
.fontFamily
);
210 userSettings
.remove("fontFamily");
212 if (globalSettings
.fontStyleModified
) {
213 globalSettings
.fontStyleModified
= false;
214 if (globalSettings
.fontStyle
!= QString("Normal"))
215 userSettings
.setValue("fontStyle", globalSettings
.fontStyle
);
217 userSettings
.remove("fontStyle");
219 if (globalSettings
.fontSizeModified
) {
220 globalSettings
.fontSizeModified
= false;
221 if (globalSettings
.fontSize
!= PmChart::defaultFontSize())
222 userSettings
.setValue("fontSize", globalSettings
.fontSize
);
224 userSettings
.remove("fontSize");
226 if (globalSettings
.savedHostsModified
) {
227 globalSettings
.savedHostsModified
= false;
228 if (globalSettings
.savedHosts
.isEmpty() == false)
229 userSettings
.setValue("savedHosts", globalSettings
.savedHosts
);
231 userSettings
.remove("savedHosts");
234 userSettings
.endGroup();
237 void checkHistory(int samples
, int visible
)
239 // sanity checking on sample sizes
240 if (samples
< PmChart::minimumPoints()) {
241 globalSettings
.sampleHistory
= PmChart::minimumPoints();
242 globalSettings
.sampleHistoryModified
= true;
244 if (samples
> PmChart::maximumPoints()) {
245 globalSettings
.sampleHistory
= PmChart::maximumPoints();
246 globalSettings
.sampleHistoryModified
= true;
248 if (visible
< PmChart::minimumPoints()) {
249 globalSettings
.visibleHistory
= PmChart::minimumPoints();
250 globalSettings
.visibleHistoryModified
= true;
252 if (visible
> PmChart::maximumPoints()) {
253 globalSettings
.visibleHistory
= PmChart::maximumPoints();
254 globalSettings
.visibleHistoryModified
= true;
256 if (samples
< visible
) {
257 globalSettings
.sampleHistory
= globalSettings
.visibleHistory
;
258 globalSettings
.sampleHistoryModified
= true;
262 static void readSettings(void)
264 QSettings userSettings
;
265 userSettings
.beginGroup(pmProgname
);
268 // Parameters related to sampling
270 globalSettings
.chartDeltaModified
= false;
271 globalSettings
.chartDelta
= userSettings
.value("chartDelta",
272 PmChart::defaultChartDelta()).toDouble();
273 globalSettings
.loggerDeltaModified
= false;
274 globalSettings
.loggerDelta
= userSettings
.value("loggerDelta",
275 PmChart::defaultLoggerDelta()).toDouble();
276 globalSettings
.sampleHistoryModified
= false;
277 globalSettings
.sampleHistory
= userSettings
.value("sampleHistory",
278 PmChart::defaultSampleHistory()).toInt();
279 globalSettings
.visibleHistoryModified
= false;
280 globalSettings
.visibleHistory
= userSettings
.value("visibleHistory",
281 PmChart::defaultVisibleHistory()).toInt();
282 checkHistory(globalSettings
.sampleHistory
, globalSettings
.visibleHistory
);
283 if (globalSettings
.sampleHistoryModified
) {
284 userSettings
.setValue("samplePoints", globalSettings
.sampleHistory
);
285 globalSettings
.sampleHistoryModified
= false;
287 if (globalSettings
.visibleHistoryModified
) {
288 userSettings
.setValue("visiblePoints", globalSettings
.visibleHistory
);
289 globalSettings
.visibleHistoryModified
= false;
293 // Everything colour (scheme) related
295 QStringList colorList
;
296 globalSettings
.defaultSchemeModified
= false;
297 if (userSettings
.contains("defaultColorScheme") == true)
298 colorList
= userSettings
.value("defaultColorScheme").toStringList();
301 << "#ffff00" << "#0000ff" << "#ff0000" << "#008000" << "#ee82ee"
302 << "#aa5500" << "#666666" << "#aaff00" << "#aa00ff" << "#aaaa7f";
303 globalSettings
.defaultScheme
.setName("#-cycle");
304 globalSettings
.defaultScheme
.setModified(false);
305 globalSettings
.defaultScheme
.setColorNames(colorList
);
307 int size
= userSettings
.beginReadArray("schemes");
308 for (int i
= 0; i
< size
; i
++) {
309 userSettings
.setArrayIndex(i
);
311 scheme
.setName(userSettings
.value("name").toString());
312 scheme
.setModified(false);
313 scheme
.setColorNames(userSettings
.value("colors").toStringList());
314 globalSettings
.colorSchemes
.append(scheme
);
316 userSettings
.endArray();
319 // Everything (else) colour related
321 globalSettings
.chartBackgroundModified
= false;
322 globalSettings
.chartBackgroundName
= userSettings
.value(
323 "chartBackgroundColor", "#6ca2c9").toString();
324 globalSettings
.chartBackground
= QColor(globalSettings
.chartBackgroundName
);
326 globalSettings
.chartHighlightModified
= false;
327 globalSettings
.chartHighlightName
= userSettings
.value(
328 "chartHighlightColor", "blue").toString();
329 globalSettings
.chartHighlight
= QColor(globalSettings
.chartHighlightName
);
332 // Toolbar user preferences
334 globalSettings
.initialToolbarModified
= false;
335 globalSettings
.initialToolbar
= userSettings
.value(
336 "initialToolbar", 1).toInt();
337 globalSettings
.nativeToolbarModified
= false;
338 globalSettings
.nativeToolbar
= userSettings
.value(
339 "nativeToolbar", 1).toInt();
340 globalSettings
.toolbarLocationModified
= false;
341 globalSettings
.toolbarLocation
= userSettings
.value(
342 "toolbarLocation", 0).toInt();
343 QStringList actionList
;
344 globalSettings
.toolbarActionsModified
= false;
345 if (userSettings
.contains("toolbarActions") == true)
346 globalSettings
.toolbarActions
=
347 userSettings
.value("toolbarActions").toStringList();
348 // else: (defaults come from the pmchart.ui interface specification)
353 globalSettings
.fontFamilyModified
= false;
354 globalSettings
.fontFamily
= userSettings
.value(
355 "fontFamily", PmChart::defaultFontFamily()).toString();
356 globalSettings
.fontStyleModified
= false;
358 globalSettings
.fontStyle
= userSettings
.value(
359 "fontStyle", "Normal").toString();
360 globalSettings
.fontSizeModified
= false;
361 globalSettings
.fontSize
= userSettings
.value(
362 "fontSize", PmChart::defaultFontSize()).toInt();
365 // Saved Hosts list preferences
367 globalSettings
.savedHostsModified
= false;
368 if (userSettings
.contains("savedHosts") == true)
369 globalSettings
.savedHosts
=
370 userSettings
.value("savedHosts").toStringList();
372 userSettings
.endGroup();
375 static void readSchemes(void)
377 QChar
sep(__pmPathSeparator());
378 QString schemes
= pmGetConfig("PCP_VAR_DIR");
379 schemes
.append(sep
).append("config");
380 schemes
.append(sep
).append("pmchart");
381 schemes
.append(sep
).append("Schemes");
383 QFileInfo
fi(schemes
);
385 OpenViewDialog::openView(schemes
.toLatin1());
388 // Get next color from given scheme or from default colors for #-cycle
389 QColor
nextColor(QString scheme
, int *sequence
)
391 QList
<QColor
> colorList
;
392 int seq
= (*sequence
)++;
394 for (int i
= 0; i
< globalSettings
.colorSchemes
.size(); i
++) {
395 if (globalSettings
.colorSchemes
[i
].name() == scheme
) {
396 colorList
= globalSettings
.colorSchemes
[i
].colors();
400 if (colorList
.size() < 2) // common case
401 colorList
= globalSettings
.defaultScheme
.colors();
402 if (colorList
.size() < 2) // idiot user!?
403 colorList
<< QColor("yellow") << QColor("blue") << QColor("red")
404 << QColor("green") << QColor("violet");
405 seq
%= colorList
.size();
406 return colorList
.at(seq
);
409 static void setupViewGlobals()
411 int w
, h
, points
, x
, y
;
413 OpenViewDialog::globals(&w
, &h
, &points
, &x
, &y
);
415 QSize size
= pmchart
->size().expandedTo(QSize(w
, h
));
416 QSize desk
= QApplication::desktop()->availableGeometry().size();
417 pmchart
->resize(size
.boundedTo(desk
));
420 QPoint pos
= pmchart
->pos();
426 if (activeGroup
->sampleHistory() < points
)
427 activeGroup
->setSampleHistory(points
);
428 activeGroup
->setVisibleHistory(points
);
433 override(int opt
, pmOptions
*opts
)
446 main(int argc
, char ** argv
)
449 int sh
= -1; /* sample history length */
450 int vh
= -1; /* visible history length */
453 struct timeval logStartTime
;
454 struct timeval logEndTime
;
460 memset(&opts
, 0, sizeof(opts
));
461 __pmtimevalNow(&opts
.origin
);
462 __pmSetProgname(argv
[0]);
463 QApplication
a(argc
, argv
);
467 opts
.flags
= PM_OPTFLAG_MULTI
| PM_OPTFLAG_MIXED
;
468 opts
.short_options
= "A:a:Cc:D:f:F:g:h:H:Ln:o:O:p:s:S:T:t:Vv:WzZ:?";
469 opts
.long_options
= longopts
;
470 opts
.short_usage
= "[options] [sources]";
471 opts
.override
= override
;
474 while ((c
= pmGetOptions(argc
, argv
, &opts
)) != EOF
) {
482 configs
.append(opts
.optarg
);
486 globalSettings
.fontFamily
= opts
.optarg
;
490 sts
= (int)strtol(opts
.optarg
, &endnum
, 10);
491 if (*endnum
!= '\0' || c
< 0) {
492 pmprintf("%s: -F requires a numeric argument\n", pmProgname
);
495 globalSettings
.fontSize
= sts
;
500 outgeometry
= opts
.optarg
;
503 case 'o': /* output image file */
504 outfile
= opts
.optarg
;
507 case 'W': /* white image background */
511 case 'v': /* visible history */
512 vh
= (int)strtol(opts
.optarg
, &endnum
, 10);
513 if (*endnum
!= '\0' || vh
< 1) {
514 pmprintf("%s: -v requires a numeric argument, larger than 1\n",
522 /* hosts from a Hosts file are added to the SavedHosts list */
524 for (int i
= 0; i
< opts
.nhosts
; i
++)
525 globalSettings
.savedHosts
.append(opts
.hosts
[i
]);
526 globalSettings
.savedHostsModified
= true;
529 if (opts
.narchives
> 0) {
530 while (opts
.optind
< argc
)
531 __pmAddOptArchive(&opts
, argv
[opts
.optind
++]);
534 for (c
= 0; c
< globalSettings
.savedHosts
.size(); c
++) {
535 const QString
&host
= globalSettings
.savedHosts
.at(c
);
536 char *name
= strdup((const char *)host
.toLatin1());
538 __pmAddOptHost(&opts
, name
);
541 while (opts
.optind
< argc
)
542 __pmAddOptHost(&opts
, argv
[opts
.optind
++]);
545 if (opts
.optind
!= argc
)
548 pmUsageMessage(&opts
);
552 /* set initial sampling interval from command line, else global setting */
553 if (opts
.interval
.tv_sec
== 0 && opts
.interval
.tv_usec
== 0)
554 __pmtimevalFromReal(globalSettings
.chartDelta
, &opts
.interval
);
556 console
= new QedConsole(opts
.origin
);
559 // Deal with user requested sample/visible points globalSettings. These
560 // (command line) override the QSettings values, for this instance
561 // of pmchart. They should not be written though, unless requested
562 // later via the Settings dialog.
564 sh
= opts
.samples
? opts
.samples
: -1;
565 if (vh
!= -1 || sh
!= -1) {
567 sh
= globalSettings
.sampleHistory
;
569 vh
= globalSettings
.visibleHistory
;
570 checkHistory(sh
, vh
);
571 if (globalSettings
.sampleHistoryModified
||
572 globalSettings
.visibleHistoryModified
) {
573 pmprintf("%s: invalid sample/visible history\n", pmProgname
);
577 globalSettings
.sampleHistory
= sh
;
578 globalSettings
.visibleHistory
= vh
;
580 console
->post("Global settings setup complete");
582 // Create all of the sources
583 liveGroup
= new GroupControl();
584 archiveGroup
= new GroupControl();
586 liveGroup
->use(PM_CONTEXT_LOCAL
, QmcSource::localHost
);
587 sts
= opts
.nhosts
+ opts
.narchives
;
588 for (c
= 0; c
< opts
.nhosts
; c
++)
589 if (liveGroup
->use(PM_CONTEXT_HOST
, opts
.hosts
[c
]) < 0)
591 for (c
= 0; c
< opts
.narchives
; c
++)
592 if (archiveGroup
->use(PM_CONTEXT_ARCHIVE
, opts
.archives
[c
]) < 0)
594 if (Lflag
== 0 && sts
== 0)
595 liveGroup
->createLocalContext();
597 console
->post("Metric group setup complete (%d hosts, %d archives)",
598 opts
.nhosts
, opts
.narchives
);
601 if (opts
.narchives
> 0)
602 archiveGroup
->useTZ();
606 else if (opts
.timezone
!= NULL
) {
607 if (opts
.narchives
> 0)
608 archiveGroup
->useTZ(QString(opts
.timezone
));
610 liveGroup
->useTZ(QString(opts
.timezone
));
611 if ((sts
= pmNewZone(opts
.timezone
)) < 0) {
612 pmprintf("%s: cannot set timezone to \"%s\": %s\n",
613 pmProgname
, (char *)opts
.timezone
, pmErrStr(sts
));
620 // Choose which Tab will be displayed initially - archive/live.
621 // If any archives given on command line, we go Archive mode;
622 // otherwise Live mode wins. Our initial pmtime connection is
623 // set in that mode too. Later we'll make a second connection
624 // in the other mode (and only "on-demand").
626 if (opts
.narchives
> 0) {
627 archiveGroup
->defaultTZ(tzLabel
, tzString
);
628 archiveGroup
->updateBounds();
629 logStartTime
= archiveGroup
->logStart();
630 logEndTime
= archiveGroup
->logEnd();
631 if ((sts
= pmParseTimeWindow(opts
.start_optarg
, opts
.finish_optarg
,
632 opts
.align_optarg
, opts
.origin_optarg
,
633 &logStartTime
, &logEndTime
, &opts
.start
,
634 &opts
.finish
, &opts
.origin
, &endnum
)) < 0) {
635 pmprintf("Cannot parse archive time window\n%s\n", endnum
);
636 pmUsageMessage(&opts
);
640 // move position to account for initial visible points
641 if (tcmp(&opts
.origin
, &opts
.start
) <= 0)
642 for (c
= 0; c
< globalSettings
.visibleHistory
- 2; c
++)
643 __pmtimevalAdd(&opts
.origin
, &opts
.interval
);
644 if (tcmp(&opts
.origin
, &opts
.finish
) > 0)
645 opts
.origin
= opts
.finish
;
648 liveGroup
->defaultTZ(tzLabel
, tzString
);
649 __pmtimevalNow(&logStartTime
);
650 logEndTime
.tv_sec
= logEndTime
.tv_usec
= INT_MAX
;
651 if ((sts
= pmParseTimeWindow(opts
.start_optarg
, opts
.finish_optarg
,
652 opts
.align_optarg
, opts
.origin_optarg
,
653 &logStartTime
, &logEndTime
, &opts
.start
,
654 &opts
.finish
, &opts
.origin
, &endnum
)) < 0) {
655 pmprintf("Cannot parse live time window\n%s\n", endnum
);
656 pmUsageMessage(&opts
);
661 console
->post("Timezones and time window setup complete");
663 globalFont
= new QFont(globalSettings
.fontFamily
, globalSettings
.fontSize
);
664 if (globalSettings
.fontStyle
.contains("Italic"))
665 globalFont
->setItalic(true);
666 if (globalSettings
.fontStyle
.contains("Bold"))
667 globalFont
->setBold(true);
670 fileIconProvider
= new QedFileIconProvider();
672 pmchart
= new PmChart
;
673 pmtime
= new TimeControl
;
675 console
->post("Phase1 user interface constructors complete");
677 // Start pmtime process for time management
678 pmtime
->init(opts
.guiport
, opts
.narchives
== 0, &opts
.interval
, &opts
.origin
,
679 &opts
.start
, &opts
.finish
, tzString
, tzLabel
);
682 liveGroup
->init(globalSettings
.sampleHistory
,
683 globalSettings
.visibleHistory
,
684 pmtime
->liveInterval(), pmtime
->livePosition());
685 archiveGroup
->init(globalSettings
.sampleHistory
,
686 globalSettings
.visibleHistory
,
687 pmtime
->archiveInterval(), pmtime
->archivePosition());
690 // We setup the pmchart tab list late, so we don't have to deal
691 // with pmtime messages reaching the Tabs until we're all setup.
693 if (opts
.narchives
== 0)
694 tab
->init(pmchart
->tabWidget(), liveGroup
, "Live");
696 tab
->init(pmchart
->tabWidget(), archiveGroup
, "Archive");
697 pmchart
->tabWidget()->insertTab(tab
);
698 pmchart
->setActiveTab(0, true);
699 console
->post("Phase2 user interface setup complete");
702 for (c
= 0; c
< configs
.size(); c
++)
703 if (!OpenViewDialog::openView((const char *)configs
[c
].toLatin1()))
710 if (Cflag
) // done with -c config, quit
715 console
->post("Top level window shown");
717 a
.connect(&a
, SIGNAL(lastWindowClosed()), pmchart
, SLOT(quit()));