[0001] [0002] [0003] [0004] [0005] [0006] [0007] [0008] [0009] [0010] [0011] [0012] [0013] [0014] [0015] [0016] [0017] [0018] [0019] [0020] [0021] [0022] [0023] [0024] [0025] [0026] [0027] [0028] [0029] [0030] [0031] [0032] [0033] [0034] [0035] [0036] [0037] [0038] [0039] [0040] [0041] [0042] [0043] [0044] [0045] [0046] [0047] [0048] [0049] [0050] [0051] [0052] [0053] [0054] [0055] [0056] [0057] [0058] [0059] [0060] [0061] [0062] [0063] [0064] [0065] [0066] [0067] [0068] [0069] [0070] [0071] [0072] [0073] [0074] [0075] [0076] [0077] [0078] [0079] [0080] [0081] [0082] [0083] [0084] [0085] [0086] [0087] [0088] [0089] [0090] [0091] [0092] [0093] [0094] [0095] [0096] [0097] [0098] [0099] [0100] [0101] [0102] [0103] [0104] [0105] [0106] [0107] [0108] [0109] [0110] [0111] [0112] [0113] [0114] [0115] [0116] [0117] [0118] [0119] [0120] [0121] [0122] [0123] [0124] [0125] [0126] [0127] [0128] [0129] [0130] [0131] [0132] [0133] [0134] [0135] [0136] [0137] [0138] [0139] [0140] [0141] [0142] [0143] [0144] [0145] [0146] [0147] [0148] [0149] [0150] [0151] [0152] [0153] [0154] [0155] [0156] [0157] [0158] [0159] [0160] [0161] [0162] [0163] [0164] [0165] [0166] [0167] [0168] [0169] [0170] [0171] [0172] [0173] [0174] [0175] [0176] [0177] [0178] [0179] [0180] [0181] [0182] [0183] [0184] [0185] [0186] [0187] [0188] [0189] [0190] [0191] [0192] [0193] [0194] [0195] [0196] [0197] [0198] [0199] [0200] [0201] [0202] [0203] [0204] [0205] [0206] [0207] [0208] [0209] [0210] [0211] [0212] [0213] [0214] [0215] [0216] [0217] [0218] [0219] [0220] [0221] [0222] [0223] [0224] [0225] [0226] [0227] [0228] [0229] [0230] [0231] [0232] [0233] [0234] [0235] [0236] [0237] [0238] [0239] [0240] [0241] [0242] [0243] [0244] [0245] [0246] [0247] [0248] [0249] [0250] [0251] [0252] [0253] [0254] [0255] [0256] [0257] [0258] [0259] [0260] [0261] [0262] [0263] [0264] [0265] [0266] [0267] [0268] [0269] [0270] [0271] [0272] [0273] [0274] [0275] [0276] [0277] [0278] [0279] [0280] [0281] [0282] [0283] [0284] [0285] [0286] [0287] [0288] [0289] [0290] [0291] [0292] [0293] [0294] [0295] [0296] [0297] [0298] [0299] [0300] [0301] [0302] [0303] [0304] [0305] [0306] [0307] [0308] [0309] [0310] [0311] [0312] [0313] [0314] [0315] [0316] [0317] [0318] [0319] [0320] [0321] [0322] [0323] [0324] [0325] [0326] [0327] [0328] [0329] [0330] [0331] [0332] [0333] [0334] [0335] [0336] [0337] [0338] [0339] [0340] [0341] [0342] [0343] [0344] [0345] [0346] [0347] [0348] [0349] [0350] [0351] [0352] [0353] [0354] [0355] [0356] [0357] [0358] [0359] [0360] [0361] [0362] [0363] [0364] [0365] [0366] [0367] [0368] [0369] [0370] [0371] [0372] [0373] [0374] [0375] [0376] [0377] [0378] [0379] [0380] [0381] [0382] [0383] [0384] [0385] [0386] [0387] [0388] [0389] [0390] [0391] [0392] [0393] [0394] [0395] [0396] [0397] [0398] [0399] [0400] [0401] [0402] [0403] [0404] [0405] [0406] [0407] [0408] [0409] [0410] [0411] [0412] [0413] [0414] [0415] [0416] [0417] [0418] [0419] [0420] [0421] [0422] [0423] [0424] [0425] [0426] [0427] [0428] [0429] [0430] [0431] [0432] [0433] [0434] [0435] [0436] [0437] [0438] [0439] [0440] [0441] [0442] [0443] [0444] [0445] [0446] [0447] [0448] [0449] [0450] [0451] [0452] [0453] [0454] [0455] [0456] [0457] [0458] [0459] [0460] [0461] [0462] [0463] [0464] [0465] [0466] [0467] [0468] [0469] [0470] [0471] [0472] [0473] [0474] [0475] [0476] [0477] [0478] [0479] [0480] [0481] [0482] [0483] [0484] [0485] [0486] [0487] [0488] [0489] [0490] [0491] [0492] [0493] [0494] [0495] [0496] [0497] [0498] [0499] [0500] [0501] [0502] [0503] [0504] [0505] [0506] [0507] [0508] [0509] [0510] [0511] [0512] [0513] [0514] [0515] [0516] [0517] [0518] [0519] [0520] [0521] [0522] [0523] [0524] [0525] [0526] [0527] [0528] [0529] [0530] [0531] [0532] [0533] [0534] [0535] [0536] [0537] [0538] [0539] [0540] [0541] [0542] [0543] [0544] [0545] [0546] [0547] [0548] [0549] [0550] [0551] [0552] [0553] [0554] [0555] [0556] [0557] [0558] [0559] [0560] [0561] [0562] [0563] [0564] [0565] [0566] [0567] [0568] [0569] [0570] [0571] [0572] [0573] [0574] [0575] [0576] [0577] [0578] [0579] [0580] [0581] [0582] [0583] [0584] [0585] [0586] [0587] [0588] [0589] [0590] [0591] [0592] [0593] [0594] [0595] [0596] [0597] [0598] [0599] [0600] [0601] [0602] [0603] [0604] [0605] [0606] [0607] [0608] [0609] [0610] [0611] [0612] [0613] [0614] [0615] [0616] [0617] [0618] [0619] [0620] [0621] [0622] [0623] [0624] [0625] [0626] [0627] [0628] [0629] [0630] [0631] [0632] [0633] [0634] [0635] [0636] [0637] [0638] [0639] [0640] [0641] [0642] [0643] [0644] [0645] [0646] [0647] [0648] [0649] [0650] [0651] [0652] [0653] [0654] [0655] [0656] [0657] [0658] [0659] [0660] [0661] [0662] [0663] [0664] [0665] [0666] [0667] [0668] [0669] [0670] [0671] [0672] [0673] [0674] [0675] [0676] [0677] [0678] [0679] [0680] [0681] [0682] [0683] [0684] [0685] [0686] [0687] [0688] [0689] [0690] [0691] [0692] [0693] [0694] [0695] [0696] [0697] [0698] [0699] [0700] [0701] [0702] [0703] [0704] [0705] [0706] [0707] [0708] [0709] [0710] [0711] [0712] [0713] [0714] [0715] [0716] [0717] [0718] [0719] [0720] [0721] [0722] [0723] [0724] [0725] [0726] [0727] [0728] [0729] [0730] [0731] [0732] [0733] [0734] [0735] [0736] [0737] [0738] [0739] [0740] [0741] [0742] [0743] [0744] [0745] [0746] [0747] [0748] [0749] [0750] [0751] [0752] [0753] [0754] [0755] [0756] [0757] [0758] [0759] [0760] [0761] [0762] [0763] [0764] [0765] [0766] [0767] [0768] [0769] [0770] [0771] [0772] [0773] [0774] [0775] [0776] [0777] [0778] [0779] [0780] [0781] [0782] [0783] [0784] [0785] [0786] [0787] [0788] [0789] [0790] [0791] [0792] [0793] [0794] [0795] [0796] [0797] [0798] [0799] [0800] [0801] [0802] [0803] [0804] [0805] [0806] [0807] [0808] [0809] [0810] [0811] [0812] [0813] [0814] [0815] [0816] [0817] [0818] [0819] [0820] [0821] [0822] [0823] [0824] [0825] [0826] [0827] [0828] [0829] [0830] [0831] [0832] [0833] [0834] [0835] [0836] [0837] [0838] [0839] [0840] [0841] [0842] [0843] [0844] [0845] [0846] [0847] [0848] [0849] [0850] [0851] [0852] [0853] [0854] [0855] [0856] [0857] [0858] [0859] [0860] [0861] [0862] [0863] [0864] [0865] [0866] [0867] [0868] [0869] [0870] [0871] [0872] [0873] [0874] [0875] [0876] [0877] [0878] [0879] [0880] [0881] [0882] [0883] [0884] [0885] [0886] [0887] [0888] [0889] [0890] [0891] [0892] [0893] [0894] [0895] [0896] [0897] [0898] [0899] [0900] [0901] [0902] [0903] [0904] [0905] [0906] [0907] [0908] [0909] [0910] [0911] [0912] [0913] [0914] [0915] [0916] [0917] [0918] [0919] [0920] [0921] [0922] [0923] [0924] [0925] [0926] [0927] [0928] [0929] [0930] [0931] [0932] [0933] [0934] [0935] [0936] [0937] [0938] [0939] [0940] [0941] [0942] [0943] [0944] [0945] [0946] [0947] [0948] [0949] [0950] [0951] [0952] [0953] [0954] [0955] [0956] [0957] [0958] [0959] [0960] [0961] [0962] [0963] [0964] [0965] [0966] [0967] [0968] [0969] [0970] [0971] [0972] [0973] [0974] [0975] [0976] [0977] [0978] [0979] [0980] [0981] [0982] [0983] [0984] [0985] [0986] [0987] [0988] [0989] [0990] [0991] [0992] [0993] [0994] [0995] [0996] [0997] [0998] [0999] [1000] [1001] [1002] [1003] [1004] [1005] [1006] [1007] [1008] [1009] [1010] [1011] [1012] [1013] [1014] [1015] [1016] [1017] [1018] [1019] [1020] [1021] [1022] [1023] [1024] [1025] [1026] [1027] [1028] [1029] [1030] [1031] [1032] [1033] [1034] [1035] [1036] [1037] [1038] [1039] [1040] [1041] [1042] [1043] [1044] [1045] [1046] [1047] [1048] [1049] [1050] [1051] [1052] [1053] [1054] [1055] [1056] [1057] [1058] [1059] [1060] [1061] [1062] [1063] [1064] [1065] [1066] [1067] [1068] [1069] [1070] [1071] [1072] [1073] [1074] [1075] [1076] [1077] [1078] [1079] [1080] [1081] [1082] [1083] [1084] [1085] [1086] [1087] [1088] [1089] [1090] [1091] [1092] [1093] [1094] [1095] [1096] [1097] [1098] [1099] [1100] [1101] [1102] [1103] [1104] [1105] [1106] [1107] [1108] [1109] [1110] [1111] [1112] [1113] [1114] [1115] [1116] [1117] [1118] [1119] [1120] [1121] [1122] [1123] [1124] [1125] [1126] [1127] [1128] [1129] [1130] [1131] [1132] [1133] [1134] [1135] [1136] [1137] [1138] [1139] [1140] [1141] [1142] [1143] [1144] [1145] [1146] [1147] [1148] [1149] [1150] [1151] [1152] [1153] [1154] [1155] [1156] [1157] [1158] [1159] [1160] [1161] [1162] [1163] [1164] [1165] [1166] [1167] [1168] [1169] [1170] [1171] [1172] [1173] [1174] [1175] [1176] [1177] [1178] [1179] [1180] [1181] [1182] [1183] [1184] [1185] [1186] [1187] [1188] [1189] [1190] [1191] [1192] [1193] [1194] [1195] [1196] [1197] [1198] [1199] [1200] [1201] [1202] [1203] [1204] [1205] [1206] [1207] [1208] [1209] [1210] [1211] [1212] [1213] [1214] [1215] [1216] [1217] [1218] [1219] [1220] [1221] [1222] [1223] [1224] [1225] [1226] [1227] [1228] [1229] [1230] [1231] [1232] [1233] [1234] [1235] [1236] [1237] [1238] [1239] [1240] [1241] [1242] [1243] [1244] [1245] [1246] [1247] [1248] [1249] [1250] [1251] [1252] [1253] [1254] [1255] [1256] [1257] [1258] [1259] [1260] [1261] [1262] [1263] [1264] [1265] [1266] [1267] [1268] [1269] [1270] [1271] [1272] [1273] [1274] [1275] [1276] [1277] [1278] [1279] [1280] [1281] [1282] [1283] [1284] [1285] [1286] [1287] [1288] [1289] [1290] [1291] [1292] [1293] [1294] [1295] [1296] [1297] [1298] [1299] [1300] [1301] [1302] [1303] [1304] [1305] [1306] [1307] [1308] [1309] [1310] [1311] [1312] [1313] [1314] [1315] [1316] [1317] [1318] [1319] [1320] [1321] [1322] [1323] [1324] [1325] [1326] [1327] [1328] [1329] [1330] [1331] [1332] [1333] [1334] [1335] [1336] [1337] [1338] [1339] [1340] [1341] [1342] [1343] [1344] [1345] [1346] [1347] [1348] [1349] [1350] [1351] [1352] [1353] [1354] [1355] [1356] [1357] [1358] [1359] [1360] [1361] [1362] [1363] [1364] [1365] [1366] [1367] [1368] [1369] [1370] [1371] [1372] [1373] [1374] [1375] [1376] [1377] [1378] [1379] [1380] [1381] [1382] [1383] [1384] [1385] [1386] [1387] [1388] [1389] [1390] [1391] [1392] [1393] [1394] [1395] [1396] [1397] [1398] [1399] [1400] [1401] [1402] [1403] [1404] [1405] [1406] [1407] [1408] [1409] [1410] [1411] [1412] [1413] [1414] [1415] [1416] [1417] [1418] [1419] [1420] [1421] [1422] [1423] [1424] [1425] [1426] [1427] [1428] [1429] [1430] [1431] [1432] [1433] [1434] [1435] [1436] [1437] [1438] [1439] [1440] [1441] [1442] [1443] [1444] [1445] [1446] [1447] [1448] [1449] [1450] [1451] [1452] [1453] [1454] [1455] [1456] [1457] [1458] [1459] [1460] [1461] [1462] [1463] [1464] [1465] [1466] [1467] [1468] [1469] [1470] [1471] [1472] [1473] [1474] [1475] [1476] [1477] [1478] [1479] [1480] [1481] [1482] [1483] [1484] [1485] [1486] [1487] [1488] [1489] [1490] [1491] [1492] [1493] [1494] [1495] [1496] [1497] [1498] [1499] [1500] [1501] [1502] [1503] [1504] [1505] [1506] [1507] [1508] [1509] [1510] [1511] [1512] [1513] [1514] [1515] [1516] [1517] [1518] [1519] [1520] [1521] [1522] [1523] [1524] [1525] [1526] [1527] [1528] [1529] [1530] [1531] [1532] [1533] [1534] [1535] [1536] [1537] [1538] [1539] [1540] [1541] [1542] [1543] [1544] [1545] [1546] [1547] [1548] [1549] [1550] [1551] [1552] [1553] [1554] [1555] [1556] [1557] [1558] [1559] [1560] [1561] [1562] [1563] [1564] [1565] [1566] [1567] [1568] [1569] [1570] [1571] [1572] [1573] [1574] [1575] [1576] [1577] [1578] [1579] [1580] [1581] [1582] [1583] [1584] [1585] [1586] [1587] [1588] [1589] [1590] [1591] [1592] [1593] [1594] [1595] [1596] [1597] [1598] [1599] [1600] [1601] [1602] [1603] [1604] [1605] [1606] [1607] [1608] [1609] [1610] [1611] [1612] [1613] [1614] [1615] [1616] [1617] [1618] [1619] [1620] [1621] [1622] [1623] [1624] [1625] [1626] [1627] [1628] [1629] [1630] [1631] [1632] [1633] [1634] [1635] [1636] [1637] [1638] [1639] [1640] [1641] [1642] [1643] [1644] [1645] [1646] [1647] [1648] [1649] [1650] [1651] [1652] [1653] [1654] [1655] [1656] [1657] [1658] [1659] [1660] [1661] [1662] [1663] [1664] [1665] [1666] [1667] [1668] [1669] [1670] [1671] [1672] [1673] [1674] [1675] [1676] [1677] [1678] [1679] [1680] [1681] [1682] [1683] [1684] [1685] [1686] [1687] [1688] [1689] [1690] [1691] [1692] [1693] [1694] [1695] [1696] [1697] [1698] [1699] [1700] [1701] [1702] [1703] [1704] [1705] [1706] [1707] [1708] [1709] [1710] [1711] [1712] [1713] [1714] [1715] [1716] [1717] [1718] [1719] [1720] [1721] [1722] [1723] [1724] [1725] [1726] [1727] [1728] [1729] [1730] [1731] [1732] [1733] [1734] [1735] [1736] [1737] [1738] [1739] [1740] [1741] [1742] [1743] [1744] [1745] [1746] [1747] [1748] [1749] [1750] [1751] [1752] [1753] [1754] [1755] [1756] [1757] [1758] [1759] [1760] [1761] [1762] [1763] [1764] [1765] [1766] [1767] [1768] [1769] [1770] [1771] [1772] [1773] [1774] [1775] [1776] [1777] [1778] [1779] [1780] [1781] [1782] [1783] [1784] [1785] [1786] [1787] [1788] [1789] [1790] [1791] [1792] [1793] [1794] [1795] [1796] [1797] [1798] [1799] [1800] [1801] [1802] [1803] [1804] [1805] [1806] [1807] [1808] [1809] [1810] [1811] [1812] [1813] [1814] [1815] [1816] [1817] [1818] [1819] [1820] [1821] [1822] [1823] [1824] [1825] [1826] [1827] [1828] [1829] [1830] [1831] [1832] [1833] [1834] [1835] [1836] [1837] [1838] [1839] [1840] [1841] [1842] [1843] [1844] [1845] [1846] [1847] [1848] [1849] [1850] [1851] [1852] [1853] [1854] [1855] [1856] [1857] [1858] [1859] [1860] [1861] [1862] [1863] [1864] [1865] [1866] [1867] [1868] [1869] [1870] [1871] [1872] [1873] [1874] [1875] [1876] [1877] [1878] [1879] [1880] [1881] [1882] [1883] [1884] [1885] [1886] [1887] [1888] [1889] [1890] [1891] [1892] [1893] [1894] [1895] [1896] [1897] [1898] [1899] [1900] [1901] [1902] [1903] [1904] [1905] [1906] [1907] [1908] [1909] [1910] [1911] [1912] [1913] [1914] [1915] [1916] [1917] [1918] [1919] [1920] [1921] [1922] [1923] [1924] [1925] [1926] [1927] [1928] [1929] [1930] [1931] [1932] [1933] [1934] [1935] [1936] [1937] [1938] [1939] [1940] [1941] [1942] [1943] [1944] [1945] [1946] [1947] [1948] [1949] [1950] [1951] [1952] [1953] [1954] [1955] [1956] [1957] [1958] [1959] [1960] [1961] [1962] [1963] [1964] [1965] [1966] [1967] [1968] [1969] [1970] [1971] [1972] [1973] [1974] [1975] [1976] [1977] [1978] [1979] [1980] [1981] [1982] [1983] [1984] [1985] [1986] [1987] [1988] [1989] [1990] [1991] [1992] [1993] [1994] [1995] [1996] [1997] [1998] [1999] [2000] [2001] [2002] [2003] [2004] [2005] [2006] [2007] [2008] [2009] [2010] [2011] [2012] [2013] [2014] [2015] [2016] [2017] [2018] [2019] [2020] [2021] [2022] [2023] [2024] [2025] [2026] [2027] [2028] [2029] [2030] [2031] [2032] [2033] [2034] [2035] [2036] [2037] [2038] [2039] [2040] [2041] [2042] [2043] [2044] [2045] [2046] [2047] [2048] [2049] [2050] [2051] [2052] [2053] [2054] [2055] [2056] [2057] [2058] [2059] [2060] [2061] [2062] [2063] [2064] [2065] [2066] [2067] [2068] [2069] [2070] [2071] [2072] [2073] [2074] [2075] [2076] [2077] [2078] [2079] [2080] [2081] [2082] [2083] [2084] [2085] [2086] [2087] [2088] [2089] [2090] [2091] [2092] [2093] [2094] [2095] [2096] [2097] [2098] [2099] [2100] [2101] [2102] [2103] [2104] [2105] [2106] [2107] [2108] [2109] [2110] [2111] [2112] [2113] [2114] [2115] [2116] [2117] [2118] [2119] [2120] [2121] [2122] [2123] [2124] [2125] [2126] [2127] [2128] [2129] [2130] [2131] [2132] [2133] [2134] [2135] [2136] [2137] [2138] [2139] [2140] [2141] [2142] [2143] [2144] [2145] [2146] [2147] [2148] [2149] [2150] [2151] [2152] [2153] [2154] [2155] [2156] [2157] [2158] [2159] [2160] [2161] [2162] [2163] [2164] [2165] [2166] [2167] [2168] [2169] [2170] [2171] [2172] [2173] [2174] [2175] [2176] [2177] [2178] [2179] [2180] [2181] [2182] [2183] [2184] [2185] [2186] [2187] [2188] [2189] [2190] [2191] [2192] [2193] [2194] [2195] [2196] [2197] [2198] [2199] [2200] [2201] [2202] [2203] [2204] [2205] [2206] [2207] [2208] [2209] [2210] [2211] [2212] [2213] [2214] [2215] [2216] [2217] [2218] [2219] [2220] [2221] [2222] [2223] [2224] [2225] [2226] [2227] [2228] [2229] [2230] [2231] [2232] [2233] [2234] [2235] [2236] [2237] [2238] [2239] [2240] [2241] [2242] [2243] [2244] [2245] [2246] [2247] [2248] [2249] [2250] [2251] [2252] [2253] [2254] [2255] [2256] [2257] [2258] [2259] [2260] [2261] [2262] [2263] [2264] [2265] [2266] [2267] [2268] [2269] [2270] [2271] [2272] [2273] [2274] [2275] [2276] [2277] [2278] [2279] [2280] [2281] [2282] [2283] [2284] [2285] [2286] [2287] [2288] [2289] [2290] [2291] [2292] [2293] [2294] [2295] [2296] [2297] [2298] [2299] [2300] [2301] [2302] [2303] [2304] [2305] [2306] [2307] [2308] [2309] [2310] [2311] [2312] [2313] [2314] [2315] [2316] [2317] [2318] [2319] [2320] [2321] [2322] [2323] [2324] [2325] [2326] [2327] [2328] [2329] [2330] [2331] [2332] [2333] [2334] [2335] [2336] [2337] [2338] [2339] [2340] [2341] [2342] [2343] [2344] [2345] [2346] [2347] [2348] [2349] [2350] [2351] [2352] [2353] [2354] [2355] [2356] [2357] [2358] [2359] [2360] [2361] [2362] [2363] [2364] [2365] [2366] [2367] [2368] [2369] [2370] [2371] [2372] [2373] [2374] [2375] [2376] [2377] [2378] [2379] [2380] [2381] [2382] [2383] [2384] [2385] [2386] [2387] [2388] [2389] [2390] [2391] [2392] [2393] [2394] [2395] [2396] [2397] [2398] [2399] [2400] [2401] [2402] [2403] [2404] [2405] [2406] [2407] [2408] [2409] [2410] [2411] [2412] [2413] [2414] [2415] [2416] [2417] [2418] [2419] [2420] [2421] [2422] [2423] [2424] [2425] [2426] [2427] [2428] [2429] [2430] [2431] [2432] [2433] [2434] [2435] [2436] [2437] [2438] [2439] [2440] [2441] [2442] [2443] [2444] [2445] [2446] [2447] [2448] [2449] [2450] [2451] [2452] [2453] [2454] [2455] [2456] [2457] [2458] [2459] [2460] [2461] [2462] [2463] [2464] [2465] [2466] [2467] [2468] [2469] [2470] [2471] [2472] [2473] [2474] [2475] [2476] [2477] [2478] [2479] [2480] [2481] [2482] [2483] [2484] [2485] [2486] [2487] [2488] [2489] [2490] [2491] [2492] [2493] [2494] [2495] [2496] [2497] [2498] [2499] [2500] [2501] [2502] [2503] [2504] [2505] [2506] [2507] [2508] [2509] [2510] [2511] [2512] [2513] [2514] [2515] [2516] [2517] [2518] [2519] [2520] [2521] [2522] [2523] [2524] [2525] [2526] [2527] [2528] [2529] [2530] [2531] [2532] [2533] [2534] [2535] [2536] [2537] [2538] [2539] [2540] [2541] [2542] [2543] [2544] [2545] [2546] [2547] [2548] [2549] [2550] [2551] [2552] [2553] [2554] [2555] [2556] [2557] [2558] [2559] [2560] [2561] [2562] [2563] [2564] [2565] [2566] [2567] [2568] [2569] [2570] [2571] [2572] [2573] [2574] [2575] [2576] [2577] [2578] [2579] [2580] [2581] [2582] [2583] [2584] [2585] [2586] [2587] [2588] [2589] [2590] [2591] [2592] [2593] [2594] [2595] [2596] [2597] [2598] [2599] [2600] [2601] [2602] [2603] [2604] [2605] [2606] [2607] [2608] [2609] [2610] [2611] [2612] [2613] [2614] [2615] [2616] [2617] [2618] [2619] [2620] [2621] [2622] [2623] [2624] [2625] [2626] [2627] [2628] [2629] [2630] [2631] [2632] [2633] [2634] [2635] [2636] [2637] [2638] [2639] [2640] [2641] [2642] [2643] [2644] [2645] [2646] [2647] [2648] [2649] [2650] [2651] [2652] [2653] [2654] [2655] [2656] [2657] [2658] [2659] [2660] [2661] [2662] [2663] [2664] [2665] [2666] [2667] [2668] [2669] [2670] [2671] [2672] [2673] [2674] [2675] [2676] [2677] [2678] [2679] [2680] [2681] [2682] [2683] [2684] [2685] [2686] [2687] [2688] [2689] [2690] [2691] [2692] [2693] [2694] [2695] [2696] [2697] [2698] [2699] [2700] [2701] [2702] [2703] [2704] [2705] [2706] [2707] [2708] [2709] [2710] [2711] [2712] [2713] [2714] [2715] [2716] [2717] [2718] [2719] [2720] [2721] [2722] [2723] [2724] [2725] [2726] [2727] [2728] [2729] [2730] [2731] [2732] [2733] [2734] [2735] [2736] [2737] [2738] [2739] [2740] [2741] [2742] [2743] [2744] [2745] [2746] [2747] [2748] [2749] [2750] [2751] [2752] [2753] [2754] [2755] [2756] [2757] [2758] [2759] [2760] [2761] [2762] [2763] [2764] [2765] [2766] [2767] [2768] [2769] [2770] [2771] [2772] [2773] [2774] [2775] [2776] [2777] [2778] [2779] [2780] [2781] [2782] [2783] [2784] [2785] [2786] [2787] [2788] [2789] [2790] [2791] [2792] [2793] [2794] [2795] [2796] [2797] [2798] [2799] [2800] [2801] [2802] [2803] [2804] [2805] [2806] [2807] [2808] [2809] [2810] [2811] [2812] [2813] [2814] [2815] [2816] [2817] [2818] [2819] [2820] [2821] [2822] [2823] [2824] [2825] [2826] [2827] [2828] [2829] [2830] [2831] [2832] [2833] [2834] [2835] [2836] [2837] [2838] [2839] [2840] [2841] [2842] [2843] [2844] [2845] [2846] [2847] [2848] [2849] [2850] [2851] [2852] [2853] [2854] [2855] [2856] [2857] [2858] [2859] [2860] [2861] [2862] [2863] [2864] [2865] [2866] [2867] [2868] [2869] [2870] [2871] [2872] [2873] [2874] [2875] [2876] [2877] [2878] [2879] [2880] [2881] [2882] [2883] [2884] [2885] [2886] [2887] [2888] [2889] [2890] [2891] [2892] [2893] [2894] [2895] [2896] [2897] [2898] [2899] [2900] [2901] [2902] [2903] [2904] [2905] [2906] [2907] [2908] [2909] [2910] [2911] [2912] [2913] [2914] [2915] [2916] [2917] [2918] [2919] [2920] [2921] [2922] [2923] [2924] [2925] [2926] [2927] [2928] [2929] [2930] [2931] [2932] [2933] [2934] [2935] [2936] [2937] [2938] [2939] [2940] [2941] [2942] [2943] [2944] [2945] [2946] [2947] [2948] [2949] [2950] [2951] [2952] [2953] [2954] [2955] [2956] [2957] [2958] [2959] [2960] [2961] [2962] [2963] [2964] [2965] [2966] [2967] [2968] [2969] [2970] [2971] [2972] [2973] [2974] [2975] [2976] [2977] [2978] [2979] [2980] [2981] [2982] [2983] [2984] [2985] [2986] [2987] [2988] [2989] [2990] [2991] [2992] [2993] [2994] [2995] [2996] [2997] [2998] [2999] [3000] [3001] [3002] [3003] [3004] [3005] [3006] [3007] [3008] [3009] [3010] [3011] [3012] [3013] [3014] [3015] [3016] [3017] [3018] [3019] [3020] [3021] [3022] [3023] [3024] [3025] [3026] [3027] [3028] [3029] [3030] [3031] [3032] [3033] [3034] [3035] [3036] [3037] [3038] [3039] [3040] [3041] [3042] [3043] [3044] [3045] [3046] [3047] [3048] [3049] [3050] [3051] [3052] [3053] [3054] [3055] [3056] [3057] [3058] [3059] [3060] [3061] [3062] [3063] [3064] [3065] [3066] [3067] [3068] [3069] [3070] [3071] [3072] [3073] [3074] [3075] [3076] [3077] [3078] [3079] [3080] [3081] [3082] [3083] [3084] [3085] [3086] [3087] [3088] [3089] [3090] [3091] [3092] [3093] [3094] [3095] [3096] [3097] [3098] [3099] [3100] [3101] [3102] [3103] [3104] [3105] [3106] [3107] [3108] [3109] [3110] [3111] [3112] [3113] [3114] [3115] [3116] [3117] [3118] [3119] [3120] [3121] [3122] [3123] [3124] [3125] [3126] [3127] [3128] [3129] [3130] [3131] [3132] [3133] [3134] [3135] [3136] [3137] [3138] [3139] [3140] [3141] [3142] [3143] [3144] [3145] [3146] [3147] [3148] [3149] [3150] [3151] [3152] [3153] [3154] [3155] [3156] [3157] [3158] [3159] [3160] [3161] [3162] [3163] [3164] [3165] [3166] [3167] [3168] [3169] [3170] [3171] [3172] [3173] [3174] [3175] [3176] [3177] [3178] [3179] [3180] [3181] [3182] [3183] [3184] [3185] [3186] [3187] [3188] [3189] [3190] [3191] [3192] [3193] [3194] [3195] [3196] [3197] [3198] [3199] [3200] [3201] [3202] [3203] [3204] [3205] [3206] [3207] [3208] [3209] [3210] [3211] [3212] [3213] [3214] [3215] [3216] [3217] [3218] [3219] [3220] [3221] [3222] [3223] [3224] [3225] [3226] [3227] [3228] [3229] [3230] [3231] [3232] [3233] [3234] [3235] [3236] [3237] [3238] [3239] [3240] [3241] [3242] [3243] [3244] [3245] [3246] [3247] [3248] [3249] [3250] [3251] [3252] [3253] [3254] [3255] [3256] [3257] [3258] [3259] [3260] [3261] [3262] [3263] [3264] [3265] [3266] [3267] [3268] [3269] [3270] [3271] [3272] [3273] [3274] [3275] [3276] [3277] [3278] [3279] [3280] [3281] [3282] [3283] [3284] [3285] [3286] [3287] [3288] [3289] [3290] [3291] [3292] [3293] [3294] [3295] [3296] [3297] [3298] [3299] [3300] [3301] [3302] [3303] [3304] [3305] [3306] [3307] [3308] [3309] [3310] [3311] [3312] [3313] [3314] [3315] [3316] [3317] [3318] [3319] [3320] [3321] [3322] [3323] [3324] [3325] [3326] [3327] [3328] [3329] [3330] [3331] [3332] [3333] [3334] [3335] [3336] [3337] [3338] [3339] [3340] [3341] [3342] [3343] [3344] [3345] [3346] [3347] [3348] [3349] [3350] [3351] [3352] [3353] [3354] [3355] [3356] [3357] [3358] [3359] [3360] [3361] [3362] [3363] [3364] [3365] [3366] [3367] [3368] [3369] [3370] [3371] [3372] [3373] [3374] [3375] [3376] [3377] [3378] [3379] [3380] [3381] [3382] [3383] [3384] [3385] [3386] [3387] [3388] [3389] [3390] [3391] [3392] [3393] [3394] [3395] [3396] [3397] [3398] [3399] [3400] [3401] [3402] [3403] [3404] [3405] [3406] [3407] [3408] [3409] [3410] [3411] [3412] [3413] [3414] [3415] [3416] [3417] [3418] [3419] [3420] [3421] [3422] [3423] [3424] [3425] [3426] [3427] [3428] [3429] [3430] [3431] [3432] [3433] [3434] [3435] [3436] [3437] [3438] [3439] [3440] [3441] [3442] [3443] [3444] [3445] [3446] [3447] [3448] [3449] [3450] [3451] [3452] [3453] [3454] [3455] [3456] [3457] [3458] [3459] [3460] [3461] [3462] [3463] [3464] [3465] [3466] [3467] [3468] [3469] [3470] [3471] [3472] [3473] [3474] [3475] [3476] [3477] [3478] [3479] [3480] [3481] [3482] [3483] [3484] [3485] [3486] [3487] [3488] [3489] [3490] [3491] [3492] [3493] [3494] [3495] [3496] [3497] [3498] [3499] [3500] [3501] [3502] [3503] [3504] [3505] [3506] [3507] [3508] [3509] [3510] [3511] [3512] [3513] [3514] [3515] [3516] [3517] [3518] [3519] [3520] [3521] [3522] [3523] [3524] [3525] [3526] [3527] [3528] [3529] [3530] [3531] [3532] [3533] [3534] [3535] [3536] [3537] [3538] [3539] [3540] [3541] [3542] [3543] [3544] [3545] [3546] [3547] [3548] [3549] [3550] [3551] [3552] [3553] [3554] [3555] [3556] [3557] [3558] [3559] [3560] [3561] [3562] [3563] [3564] [3565] [3566] [3567] [3568] [3569] [3570] [3571] [3572] [3573] [3574] [3575] [3576] [3577] [3578] [3579] [3580] [3581] [3582] [3583] [3584] [3585] [3586] [3587] [3588] [3589] [3590] [3591] [3592] [3593] [3594] [3595] [3596] [3597] [3598] [3599] [3600] [3601] [3602] [3603] [3604] [3605] [3606] [3607] [3608] [3609] [3610] [3611] [3612] [3613] [3614] [3615] [3616] [3617] [3618] [3619] [3620] [3621] [3622] [3623] [3624] [3625] [3626] [3627] [3628] [3629] [3630] [3631] [3632] [3633] [3634] [3635] [3636] [3637] [3638] [3639] [3640] [3641] [3642] [3643] [3644] [3645] [3646] [3647] [3648] [3649] [3650] [3651] [3652] [3653] [3654] [3655] [3656] [3657] [3658] [3659] [3660] [3661] [3662] [3663] [3664] [3665] [3666] [3667] [3668] [3669] [3670] [3671] [3672] [3673] [3674] [3675] [3676] [3677] [3678] [3679] [3680] [3681] [3682] [3683] [3684] [3685] [3686] [3687] [3688] [3689] [3690] [3691] [3692] [3693] [3694] [3695] [3696] [3697] [3698] [3699] [3700] [3701] [3702] [3703] [3704] [3705] [3706] [3707] [3708] [3709] [3710] [3711] [3712] [3713] [3714] [3715] [3716] [3717] [3718] [3719] [3720] [3721] [3722] [3723] [3724] [3725] [3726] [3727] [3728] [3729] [3730] [3731] [3732] [3733] [3734] [3735] [3736] [3737] [3738] [3739] [3740] [3741] [3742] [3743] [3744] [3745] [3746] [3747] [3748] [3749] [3750] [3751] [3752] [3753] [3754] [3755] [3756] [3757] [3758] [3759] [3760] [3761] [3762] [3763] [3764] [3765] [3766] [3767] [3768] [3769] [3770] [3771] [3772] [3773] [3774] [3775] [3776] [3777] [3778] [3779] [3780] [3781] [3782] [3783] [3784] [3785] [3786] [3787] [3788] [3789] [3790] [3791] [3792] [3793] [3794] [3795] [3796] [3797] [3798] [3799] [3800] [3801] [3802] [3803] [3804] [3805] [3806] [3807] [3808] [3809] [3810] [3811] [3812] [3813] [3814] [3815] [3816] [3817] [3818] [3819] [3820] [3821] [3822] [3823] [3824] [3825] [3826] [3827] [3828] [3829] [3830] [3831] [3832] [3833] [3834] [3835] [3836] [3837] [3838] [3839] [3840] [3841] [3842] [3843] [3844] [3845] [3846] [3847] [3848] [3849] [3850] [3851] [3852] [3853] [3854] [3855] [3856] [3857] [3858] [3859] [3860] [3861] [3862] [3863] [3864] [3865] [3866] [3867] [3868] [3869] [3870] [3871] [3872] [3873] [3874] [3875] [3876] [3877] [3878] [3879] [3880] [3881] [3882] [3883] [3884] [3885] [3886] [3887] [3888] [3889] [3890] [3891] [3892] [3893] [3894] [3895] [3896] [3897] [3898] [3899] [3900] [3901] [3902] [3903] [3904] [3905] [3906] [3907] [3908] [3909] [3910] [3911] [3912] [3913] [3914] [3915] [3916] [3917] [3918] [3919] [3920] [3921] [3922] [3923] [3924] [3925] [3926] [3927] [3928] [3929] [3930] [3931] [3932] [3933] [3934] [3935] [3936] [3937] [3938] [3939] [3940] [3941] [3942] [3943] [3944] [3945] [3946] [3947] [3948] [3949] [3950] [3951] [3952] [3953] [3954] [3955] [3956] [3957] [3958] [3959] [3960] [3961] [3962] [3963] [3964] [3965] [3966] [3967] [3968] [3969] [3970] [3971] [3972] [3973] [3974] [3975] [3976] [3977] [3978] [3979] [3980] [3981] [3982] [3983] [3984] [3985] [3986] [3987] [3988] [3989] [3990] [3991] [3992] [3993] [3994] [3995] [3996] [3997] [3998] [3999] [4000] [4001] [4002] [4003] [4004] [4005] [4006] [4007] [4008] [4009] [4010] [4011] [4012] [4013]
/*****************************************************************************/ /* HyperSpi.c This application generates VMS System Performance Information HTML documents. Coupled with 'HyperSpi$agent.c', the data-collection application, it can profile, complete with graphics, fundamental system performance indicators as CPU usage, memory usage, IO values. It operates in two distinct modes, text and graphic. In text mode it returns an HTML stream to the browser comprising a hypertext page, with selection menu, node performance presentation page, data listing or dump page. In graphic mode it returns a GIF image to the browser, first processing the specified data into an in-memory bitmap graph, then sending this image to the client via an internal GIF processor. A node preformance presentation page may have one or more HTML <IMG...> tags within it. Each of these tags will separately cause the browser to invoke the application to generate a graphic from the data specified in the SRC= URL. If the /NODES= qualifier specifies a comma-separated list of node names these are used in the menus for listing the available nodes. If this is not supplied the data directory is searched for current-day data files, those found have the respective node name extacted and these are used to list the nodes. The /STYLE= qualifier provide for a URL to a site-local style file. This allows the internal styles to be over-loaded. Button labels are customizable (potentially to non-English language). They comprise a label, equate symbol and URL-style path suitable for creating a link. Multiple buttons are separated using the semicolon. Note that any such button customization must provide escaped HTML-forbidden characters in the button label and URI-forbidden characters in the path! See DEFAULT_BUTTONS. Here is an example of changing the button labels: /BUTTON="Selector$Help=/hyperspi/-/hyperspi.html" Always have the equivalent of "Close" for the first button! Additional buttons may be created by adding "label=path;" elements to the button string. In this way an additional information page could be referenced as follows: /BUTTON="↶Back=javascript:parent.history.go(-1)$" + "Selector$^Help=/hyperspi/-/hyperspi.html$^Other VMS=/vms/" DIFFICULTY FITTING ALL THESE QUALIFIERS INTO A COMMAND LINE OR LOGICAL? Use an existing, or create a new, DCL wrapper procedure for the script (same name and directory) and build up a DCL symbol to provide the information. Up to 1024 characters can be included in this way. For example: $ HYPERSPI$PARAM = "/BUTTON=""Select$Help=/hyperspi/-/hyperspihelp.html""" $ HYPERSPI$PARAM = HYPERSPI$PARAM + "/STYLE=""/hyperspi/-/local.css""" $ RUN HT_EXE:HYPERSPI OSU ENVIRONMENT --------------- Script responses are returned in OSU "raw" mode; the script taking care of the full response header and correctly carriage-controlled data stream, text or binary!! Uses the CGILIB.C to engage in the dialog phase generating, storing and then making available the equivalent of CGI variables. "VANILLA" CGI ENVIRONMENT ------------------------- Primarily for the likes of Netscape FastTrack. This environment can accomodate CGI variables that are not prefixed with "WWW_" and do not supply "KEY_xxxxx" or "FORM_xxxxx" (which must be derived from "QUERY_STRING"). Full HTTP stream (non-parsed header) is assumed as not supported so all output occurs with a CGI-compliant header line (e.g. "Status: 200 Success") and record-oriented output. CGI VARIABLES ------------- FORM_BUFFERED_IO if non-null then buffered IO requested FORM_CPU if non-null then cpu usage requested FORM_DO what to do(!): "DUMP", "GRAPH", "LIST", "PAGE", "MENU" FORM_DAY the (first) day of the data file(s) FORM_DIRECT_IO if non-null then direct IO requested FORM_EXTRACT if non-null provide a link to allow graph extraction FORM_HOUR the (first) hour of the data FORM_INCLUDE additional category: PEAK, TOTAL (only with "user-mode-cpu"), HARD-FAULTS (only with "faults") FORM_LIST_NODE name of node to process data (see also FORM_NODE) FORM_MINUTE the (first) minute of the data FORM_MONTH the (first) month of the data file(s) FORM_MSCP_IO if non-null then MSCP IO requested FORM_NET_INT network interface traffic FORM_NODE name of node to process data (used before FORM_LIST_NODE) FORM_PERIOD standard period (e.g. "until" now, "business", "today", "yesterday", or since a number of days) FORM_REFRESH integer number of minutes between summary refreshes FORM_TODAY the last day of the data file(s) FORM_TOMINUTE the last minute of the data FORM_TOHOUR the last hour of the data FORM_TOMONTH the last month of the data file(s) FORM_TOYEAR the last year of the data file(s) FORM_USER_MODE_CPU if non-null then user-mode-cpu usage requested FORM_WHAT any data category can be placed in this comma-separated list FORM_XMAG number of times X axis should be enlarged FORM_YEAR the (first) year of the data file(s) FORM_YMAG number of times Y axis should be enlarged QUALIFIERS ---------- /BUTTONS= string containing button labels/paths /CHARSET= "Content-Type: text/html; charset=...", empty suppresses charset /DBUG turns on all "if (Debug)" statements /DIRECTORY= directory containing any node description HTML files /HELP= help HTML file URL /NODES= comma-separated list of node names collecting SPI data /SHUTDOWN causes the HYPERSPI$AGENT image/process to exit LOGICAL NAMES ------------- HYPERSPI$DATA locates the data files HYPERSPI$PARAM equivalent to (overrides) the command line parameters/qualifiers (define as a system-wide logical) BUILD DETAILS ------------- See BUILD_HYPERSPI.COM procedure. COPYRIGHT --------- Copyright (C) 1995-2021 Mark G.Daniel Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. VERSION HISTORY (update SOFTWAREVN as well!) --------------- 14-OCT-2014 MGD v2.1.0, a nod to the twenty-first century 21-MAY-2011 MGD v2.0.0, add network interface data data format version 3 (shared with HyperSPI++) (There is a lot of work that *could* be done to this older piece of code but I've spent the minimal time necessary just to make it work on modern platforms.) plus a few tweaks (couldn't resist :-) 10-MAY-2005 MGD v1.8.4, SWS 2.0 ignore query string components supplied as command-line parameters differently to CSWS 1.2/3 23-DEC-2003 MGD v1.8.3, minor conditional mods to support IA64 12-APR-2003 MGD v1.8.2, link colour changed to 0000cc 16-AUG-2002 MGD v1.8.1, some accomodations for CSWS v1.2 09-SEP-2001 MGD v1.8.0, provide /SHUTDOWN for the agent via $FORCEX 01-JUL-2001 MGD v1.7.1, add 'SkipParameters' for direct OSU support 28-OCT-2000 MGD v1.7.0, use CGILIB object module 12-APR-2000 MGD v1.6.1, minor changes for CGILIB 1.4 07-AUG-1999 MGD v1.6.0, use more of the CGILIB functionality 24-APR-1999 MGD v1.5.0, use CGILIB.C, standard CGI environment (Netscape FastTrack), modify to use CgiVar() 02-OCT-1998 MGD v1.4.0, provide content-type "; charset=..." 13-AUG-1998 MGD v1.3.0, accomodations for OSU bugfix; IncludeFile() file name length 13-AUG-1998 MGD NOTE: this application is not Y2K compliant (in the sense data is stored in files named with a 2 digit year!) 06-MAY-1998 MGD v1.2.0, general maintenance, cosmetic changes 11-SEP-1997 MGD v1.1.1, upped MAX_NODE_COUNT_BEFORE_SELECT to 20 01-AUG-1997 MGD v1.1.0, added /BODY= qualifier, remove extraneous '/' from <IMG SRC=...>, general maintenance 19-SEP-1995 MGD v1.0.1, replace <CR><LF> carriage-control single newline, still acceptable for HTTP, slightly more efficient 20-JUN-1995 MGD v1.0.0, initial development */ /*****************************************************************************/ #define SOFTWAREVN "2.1.0" #define SOFTWARENM "HYPERSPI" #define SOFTWARECR "Copyright (C) 1996-2020 Mark G.Daniel" #ifdef __ALPHA # define SOFTWAREID SOFTWARENM " AXP-" SOFTWAREVN #endif #ifdef __ia64 # define SOFTWAREID SOFTWARENM " IA64-" SOFTWAREVN #endif #ifdef __VAX # define SOFTWAREID SOFTWARENM " VAX-" SOFTWAREVN #endif #ifdef __x86_64 # define SOFTWAREID SOFTWARENM " X86-" SOFTWAREVN #endif /* standard C header files */ #include <ctype.h> #include <errno.h> #include <lnmdef.h> #include <stdarg.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <time.h> #include <unixio.h> /* VMS related header files */ #include <descrip.h> #include <libdef.h> #include <libdtdef.h> #include <rmsdef.h> #include <rms.h> #include <starlet.h> #include <syidef.h> #include <ssdef.h> #include <stsdef.h> /* application header file */ #include "HyperSpi.h" #include <cgilib.h> #ifndef __VAX # pragma nomember_alignment #endif #define boolean int #define true 1 #define false 0 #define VMSok(x) ((x) & STS$M_SUCCESS) #define VMSnok(x) !(((x) & STS$M_SUCCESS)) #define FI_LI __FILE__, __LINE__ /* for use by functions in PlotSpi.c */ #define PLOT_ON 0 #define PLOT_OFF 1 #define PLOT_XOR 2 #define MAX_SAMPLE_PERIOD_DAYS 31 #define MAX_NODE_COUNT_BEFORE_SELECT 20 #define DEFAULT_BUTTONS "\ ↶Back=javascript:parent.history.go(-1)$Selector$\ ^Help=/hyperspi/-/hyperspi.html\ " #define DEFAULT_STYLE "\ body { margin:0.5em; background-color:white; color:black; \ font-family:sans-serif; }\n\ a { text-decoration:none; color:black; \ border:1px solid slategray; border-radius:3px; padding:1px 5px 2px 5px; }\n\ .header { padding:0.5em 0.5em 0.5em 1em; background-color: gainsboro; \ border:1px solid lightslategray; border-radius:1px; \ font-size: 140%; font-weight: bold; word-spacing:0.2em; }\n\ .buttonbar { padding:0.7em 1em 0.7em 1em; \ border:1px solid lightslategray; border-radius:1px; \ background-color: gainsboro; min-height:1.3em; }\n\ .buttonbar a { text-decoration:none; color:inherit; \ border:1px solid slategray; border-radius:3px; padding:1px 5px 2px 5px; }\n\ .hyperSPI { display:table-cell; \ font-family: futura, \"Tw Cen MT\", helvetica, arial, sans; \ font-weight:normal; font-size:125%; letter-spacing:-2px; }\n\ .heading { display:table-cell; width:95%; text-align:center; \ font-size:110%; }\n\ .datetime { display:table-cell; white-space:nowrap; font-size:80%; \ word-spacing:0px; }\n\ .content { margin:1.5em; }\n\ .precis { margin:0.5em 1em 0.5em 2em; }\n\ .precis th { text-align:right; }\n\ .precis td { text-align:left; }\n\ .graph { margin:0.5em; }\n\ .graphtitle { margin:1em 0.5em 0.5em 0.5em; font-size:120%; font-weight:bold; \ text-decoration:underline; }\n\ .graphdata { margin:1em 1em 1em 2em; }\n\ .fileday { margin-bottom:1em; font-size:120%; font-weight:bold; }\n\ .filename { font-family:monospace; font-size:120%; }\n\ .extrbtn { margin-left:2em; background-color:whitesmoke; font-size:80%; }\n\ " char Utility [] = "HYPERSPI"; char *DayName [] = { "", "Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", }; char *MonthName [] = { "", "January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December" }; boolean Debug, DoDump, DoGraph, DoList, DoComprehensiveMenu, DoShutdownAgent, DoSummaryMenu, DoPresentSummaryPage, ErrorReported, ExtractGraph, GifTransparentBackground = true, HttpHasBeenOutput, IncludePeak, IncludeTotal, ProvidePercentCPU, ProvidePercentUserModeCPU, ProvideBufferedIO, ProvidePeakBufferedIO, ProvideDirectIO, ProvidePeakDirectIO, ProvideMemory, ProvideMscpIO, ProvidePeakMscpIO, ProvideNetInt, ProvidePeakNetInt, ProvidePageFaults, ProvidePeakPageFaults, ProvideHardPageFaults, ProvidePeakHardPageFaults, ProvideProcesses, OsuEnvironment, StdCgiEnvironment; int AvePercentCPU, AvePercentUserModeCPU, AveBufferedIO, AveDirectIO, AveLck, AveLckIn, AveLckOut, AveLckLoc, AveMscpIO, AveNetIntRxTx, AveNumberOfCom, AvePageFaults, AveHardPageFaults, AveNumberOfProcesses, AveSystemMemoryPercentInUse, AvePageSpacePercentInUse, CurrentJulianDate, CurrentMinuteFromStartOfPeriod, DataFileNameLength, DataFileSpecLength, DataFilesFoundCount, DataFilesProcessedCount, DataRecordsReadCount, DataRecordsProcessedCount, DataMinute, DataHour, DataDay, DataMonth, DataYear, FromJulianDate, FromDay, FromHour, FromMinute, FromMonth, FromYear, HaveDataY, MaxAveBufferedIO, MaxAveDirectIO, MaxAveMscpIO, MaxAveNetIntRxTx, MaxAvePageFaults, MaxAveHardPageFaults, MaxNumberOfCom, MaxNumberOfProcesses, MaxPageSpacePercentInUse, MaxSystemMemoryPercentInUse, NodeCount, NumberOfCPUs, NumberOfDays, NumberOfDaysIntoData, NumberOfHours, NumberOfMinutesIntoData, NumberOfProcesses, PageSpaceMBytes, PageSpacePercentInUse, PeakPercentUserModeCPU, PeakBufferedIO, PeakDirectIO, PeakMscpIO, PeakPageFaults, PeakNetIntRxTx, PeakPercentCPU, PeakHardPageFaults, PercentCPU, PercentUserModeCPU, RecordSampleRate, RefreshSeconds = 0, SizeOfMarginX = 10, SizeOfMarginY = 20, SizeOfPlotX, SizeOfPlotY, StartMinuteOnFirstDay, StartMinuteOfData, SystemMemoryMBytes, SystemMemoryPercentInUse, ToJulianDate, ToMinute, ToHour, ToDay, ToMonth, ToYear, XMag, YMag; #ifndef __VAX unsigned __int64 NetIntRxTx; #else unsigned int NetIntRxTx; #endif unsigned int AvePercentModeCPU[HYPERSPI_CPU_MODE_COUNT]; float ScalingFactorY; char DataFileName [256], DataFileSpec [256], DataNode [32], DateString [32], DefaultButtons [] = DEFAULT_BUTTONS, FileSpecification [256], HyperSpiUrl [256], SoftwareID [48], ToDateString [32], UnixDateTime [64]; char *ButtonsPtr = DefaultButtons, *CgiEnvironmentPtr, *CgiFormDoPtr, *CgiFormIncludePtr, *CgiFormNodePtr, *CgiFormPeriodPtr, *CgiFormRefreshPtr, *CgiFormWhatPtr, *CgiRequestMethodPtr, *CgiPathInfoPtr, *CgiPathTranslatedPtr, *CgiScriptNamePtr, *CgiServerNamePtr, *CgiServerSoftwarePtr, *CliCharsetPtr, *DefaultStyle = DEFAULT_STYLE, *HyperSpiDirectoryPtr = "HYPERSPI$DATA:", *SelectorPtr, *SoftwareCopy = SOFTWARECR, *StyleSheetPtr = ""; char SpiNodeNames [16][16] = { "" }; unsigned long CurrentBinTime [2]; unsigned short CurrentNumTime [7]; /* this structure is declared in HyperSpi.h */ struct HyperSpiData SpiRecord; /* external storage (declared in PlotSpi.c) */ extern boolean DoPlotDiagnostic; extern int PixelPlotOperation, PlotScaleY; /* function prototypes for graphing (from PlotSpi.c) */ int PlotAllocate (int XSize, int YSize); int PlotAxes (int Operation); int PlotPixel (int Operation, int AtX, int AtY, int XOrigin, int YOrigin); int PlotLineX (int Operation, int FromX, int ToX, int AtY, int XOrigin, int YOrigin); int PlotLineY (int Operation, int FromY, int ToY, int AtX, int XOrigin, int YOrigin); /* required function prototypes */ char* ButtonBarButton (char*, char*); void DumpData(); void DumpRecord(); void ListProcessedRecord(); int GraphImageLink (int, char*, char*, ...); int GraphRecordCPU(); int GraphRecordMemory(); int GraphRecordProcesses(); int GraphRecordRange(); int ProvideScaling (); void SetScaling (int); int SelectNodeNameByDataFileName(); void SummarizeRecord(); char* SystemNodeName (); char* UniqueNumber(); /*****************************************************************************/ /* 'argc/argv' are only required to support OSU, in particular CgiLibOsuInit(). */ main ( int argc, char *argv[] ) { int status; unsigned long UnixTime; struct tm *UnixTmPtr; /*********/ /* begin */ /*********/ sprintf (SoftwareID, "%s (%s)", SOFTWAREID, CgiLibEnvironmentVersion()); if (getenv ("HYPERSPI$DBUG") != NULL) { Debug = true; fputs ("Content-Type: text/plain\n\n", stdout); CgiLibEnvironmentSetDebug (Debug); } CgiLibEnvironmentInit (argc, argv, false); CgiEnvironmentPtr = CgiLibEnvironmentName (); GetParameters (); if (DoShutdownAgent) exit (ShutdownAgent()); CgiLibResponseSetCharset (CliCharsetPtr); CgiLibResponseSetSoftwareID (SoftwareID); CgiLibResponseSetErrorMessage ("Reported by HyperSPI"); if (StyleSheetPtr[0]) { char *cptr = calloc (1, 64+strlen(StyleSheetPtr)); sprintf (cptr, "<link rel=\"stylesheet\" \ type=\"text/css\" href=\"%s\">\n", StyleSheetPtr); StyleSheetPtr = cptr; } CgiServerSoftwarePtr = CgiVar ("WWW_SERVER_SOFTWARE"); CgiRequestMethodPtr = CgiVar ("WWW_REQUEST_METHOD"); if (strcmp (CgiRequestMethodPtr, "GET")) { CgiLibResponseHeader (501, "text/html"); fprintf (stdout, "Not implemented!\n"); return; } CgiScriptNamePtr = CgiVar ("WWW_SCRIPT_NAME"); CgiServerNamePtr = CgiVar ("WWW_SERVER_NAME"); sys$gettim (&CurrentBinTime); sys$numtim (&CurrentNumTime, &CurrentBinTime); time (&UnixTime); UnixTmPtr = localtime (&UnixTime); if (!strftime (UnixDateTime, sizeof(UnixDateTime), "%a, %d %b %Y %T", UnixTmPtr)) strcpy (UnixDateTime, "[error]"); if (Debug) fprintf (stdout, "UnixDateTime |%s|\n", UnixDateTime); GetRequest (); ValidateRequest (); if (DoSummaryMenu || DoComprehensiveMenu) { SummaryMenu (); exit (SS$_NORMAL); } /* create file specification for processing data */ DataFileSpecLength = sprintf (DataFileSpec, "%sHYPERSPI_%s_%s_%%%%%%%%%%%%.DAT", HYPERSPI_DATA_DIRECTORY, HYPERSPI_DATA_VERSION, CgiFormNodePtr); if (Debug) fprintf (stdout, "DataFileSpec |%s|\n", DataFileSpec); if (DoPresentSummaryPage) PresentSummaryPage (); else if (DoDump) DumpData (); else if (DoGraph) GraphData (); else if (DoList) ListProcessedData (); exit (SS$_NORMAL); } /*****************************************************************************/ /* Get "command-line" parameters, whether from the command-line or from a configuration symbol or logical containing the equivalent. OSU scripts have the 'method', 'url' and 'protocol' supplied as P1, P2, P3 (these being detected and used by CGILIB), and are of no interest to this function. */ GetParameters () { static char CommandLine [256]; static unsigned long Flags = 0; int idx, status, SkipParameters; unsigned short Length; char ch; char *aptr, *cptr, *clptr, *sptr; $DESCRIPTOR (CommandLineDsc, CommandLine); /*********/ /* begin */ /*********/ if ((clptr = getenv ("HYPERSPI$PARAM")) == NULL) { /* get the entire command line following the verb */ if (VMSnok (status = lib$get_foreign (&CommandLineDsc, 0, &Length, &Flags))) exit (status); (clptr = CommandLine)[Length] = '\0'; } /* if [C]SWS (VMS Apache) */ if (CgiLibEnvironmentIsApache()) { /* CSWS 1.2/3 look for something non-space outside of quotes */ for (cptr = clptr; *cptr; cptr++) { if (isspace(*cptr)) continue; if (*cptr != '\"') break; cptr++; while (*cptr && *cptr != '\"') cptr++; if (*cptr) cptr++; } /* CSWS 1.2/3 if nothing outside of quotes then ignore command line */ if (!*cptr) return; /* SWS 2.0 doesn't begin with /APACHE from DCL procedure wrapper */ if (!strsame (cptr, "/APACHE", 7)) return; } /* if OSU environment then skip P1, P2, P3 */ if (CgiLibEnvironmentIsOsu()) SkipParameters = 3; else SkipParameters = 0; aptr = NULL; ch = *clptr; for (;;) { if (aptr != NULL) *aptr = '\0'; if (!ch) break; *clptr = ch; if (Debug) fprintf (stdout, "clptr |%s|\n", clptr); while (*clptr && isspace(*clptr)) *clptr++ = '\0'; aptr = clptr; if (*clptr == '/') clptr++; while (*clptr && !isspace (*clptr) && *clptr != '/') { if (*clptr != '\"') { clptr++; continue; } cptr = clptr; clptr++; while (*clptr) { if (*clptr == '\"') if (*(clptr+1) == '\"') clptr++; else break; *cptr++ = *clptr++; } *cptr = '\0'; if (*clptr) clptr++; } ch = *clptr; if (*clptr) *clptr = '\0'; if (Debug) fprintf (stdout, "aptr |%s|\n", aptr); if (!*aptr) continue; if (SkipParameters) { SkipParameters--; continue; } if (strsame (aptr, "/APACHE", 4)) { /* just skip this marker for command-line parameters under SWS 2.0 */ continue; } if (strsame (aptr, "/BUTTONS=", 4)) { for (cptr = aptr; *cptr && *cptr != '='; cptr++); if (*cptr) cptr++; ButtonsPtr = cptr; continue; } if (strsame (aptr, "/CHARSET=", 4)) { for (cptr = aptr; *cptr && *cptr != '='; cptr++); if (*cptr) cptr++; CliCharsetPtr = cptr; continue; } if (strsame (aptr, "/DBUG", -1)) { Debug = true; continue; } if (strsame (aptr, "/DIRECTORY=", 3)) { for (cptr = aptr; *cptr && *cptr != '='; cptr++); if (*cptr) cptr++; HyperSpiDirectoryPtr = cptr; continue; } if (strsame (aptr, "/NODES=", 3)) { for (cptr = aptr; *cptr && *cptr != '='; cptr++); if (*cptr) cptr++; /* quick and nasty, no checks on array bounds (caveat emptor) */ idx = 0; while (*cptr) { sptr = SpiNodeNames[idx++]; while (*cptr && *cptr != ',') *sptr++ = toupper(*cptr++); *sptr = '\0'; if (*cptr) cptr++; } SpiNodeNames[idx][0] = '\0'; continue; } if (strsame (aptr, "/SHUTDOWN", 3)) { DoShutdownAgent = true; continue; } if (*aptr != '/') { fprintf (stdout, "%%%s-E-MAXPARM, too many parameters\n \\%s\\\n", Utility, aptr); exit (STS$K_ERROR | STS$M_INHIB_MSG); } else { fprintf (stdout, "%%%s-E-IVQUAL, unrecognized qualifier\n \\%s\\\n", Utility, aptr+1); exit (STS$K_ERROR | STS$M_INHIB_MSG); } } } /*****************************************************************************/ /* Create the navigation buttons. */ ButtonBar (int Top1Bottom2) { int idx; char *cptr, *sptr; /*********/ /* begin */ /*********/ if (Debug) fprintf (stdout, "ButtonBar()\n"); fprintf (stdout, "<div class=\"buttonbar%s\">\n", Top1Bottom2 == 1 ? " butbartop" : ""); cptr = ButtonsPtr; /* back */ cptr = ButtonBarButton (cptr, NULL); /* calendar request form */ cptr = ButtonBarButton (cptr, CgiScriptNamePtr); /* any further buttons */ while (*cptr) cptr = ButtonBarButton (cptr, NULL); fprintf (stdout, "</div>\n"); } /*****************************************************************************/ /* Generate a single "button" inside the context created by ButtonBar(). */ char* ButtonBarButton ( char *ButtonLabel, char *ButtonPath ) { char *cptr, *sptr, *tptr, *uptr; /*********/ /* begin */ /*********/ if (Debug) fprintf (stdout, "ButtonBarButton() |%s|\n", ButtonLabel); if (!*ButtonLabel) for (cptr = ButtonLabel = "*ERROR*"; *cptr; cptr++); else for (cptr = ButtonLabel; *cptr && *cptr != '=' && *cptr != '$'; cptr++); if (*ButtonLabel == '^') { ButtonLabel++; tptr = " target=\"_blank\""; } else tptr = ""; if (*cptr == '=') for (sptr = uptr = cptr+1; *sptr && *sptr != '$'; sptr++); else if (ButtonPath) sptr = (uptr = ButtonPath) + strlen(ButtonPath); else sptr = (uptr = "*ERROR*") + 8; if (!memcmp(uptr,"javascript:", 11)) fprintf (stdout, "<script type=\"text/javascript\">\ document.write(\'<a%s href=\"%*.*s\">%*.*s</a>\\n\');\ </script>\n", tptr, sptr-uptr, sptr-uptr, uptr, cptr-ButtonLabel, cptr-ButtonLabel, ButtonLabel); else fprintf (stdout, "<a%s href=\"%*.*s\">%*.*s</a>\n", tptr, sptr-uptr, sptr-uptr, uptr, cptr-ButtonLabel, cptr-ButtonLabel, ButtonLabel); while (*cptr && *cptr != '$') cptr++; while (*cptr && *cptr == '$') cptr++; return (cptr); } /*****************************************************************************/ /* Get the request CGI variables. */ GetRequest () { char *cptr; /*********/ /* begin */ /*********/ if (Debug) fprintf (stdout, "GetRequest()\n"); /* determine what has been requested */ CgiFormWhatPtr = CgiVar ("WWW_FORM_WHAT"); for (cptr = CgiFormWhatPtr; *cptr; cptr++) *cptr = toupper(*cptr); CgiFormRefreshPtr = CgiVar ("WWW_FORM_REFRESH"); RefreshSeconds = atoi(CgiFormRefreshPtr) * 60; if (RefreshSeconds < 0 || RefreshSeconds > 3600) RefreshSeconds = 0; if (strsame (CgiFormWhatPtr, "CPU", -1)) ProvidePercentCPU = true; if (strstr (CgiFormWhatPtr, ",CPU") != NULL) ProvidePercentCPU = true; cptr = CgiVar ("WWW_FORM_CPU"); if (cptr[0]) ProvidePercentCPU = true; if (strstr (CgiFormWhatPtr, "USER_MODE") != NULL) ProvidePercentUserModeCPU= true; cptr = CgiVar ("WWW_FORM_USER_MODE_CPU"); if (cptr[0]) ProvidePercentUserModeCPU = true; if (strstr (CgiFormWhatPtr, "BUFFERED") != NULL) ProvideBufferedIO = true; cptr = CgiVar ("WWW_FORM_BUFFERED_IO"); if (cptr[0]) ProvideBufferedIO = true; if (strstr (CgiFormWhatPtr, "PEAK_BUFFERED") != NULL) ProvidePeakBufferedIO = true; cptr = CgiVar ("WWW_FORM_PEAK_BUFFERED_IO"); if (cptr[0]) ProvidePeakBufferedIO = true; if (strstr (CgiFormWhatPtr, "DIRECT") != NULL) ProvideDirectIO = true; cptr = CgiVar ("WWW_FORM_DIRECT_IO"); if (cptr[0]) ProvideDirectIO = true; if (strstr (CgiFormWhatPtr, "PEAK_DIRECT") != NULL) ProvidePeakDirectIO = true; cptr = CgiVar ("WWW_FORM_PEAK_DIRECT_IO"); if (cptr[0]) ProvidePeakDirectIO = true; if (strstr (CgiFormWhatPtr, "MSCP") != NULL) ProvideMscpIO = true; cptr = CgiVar ("WWW_FORM_MSCP_IO"); if (cptr[0]) ProvideMscpIO = true; if (strstr (CgiFormWhatPtr, "PEAK_MSCP") != NULL) ProvidePeakMscpIO = true; cptr = CgiVar ("WWW_FORM_PEAK_MSCP_IO"); if (cptr[0]) ProvidePeakMscpIO = true; if (strstr (CgiFormWhatPtr, "NET_INT") != NULL) ProvideNetInt = true; cptr = CgiVar ("WWW_FORM_NET_INT"); if (cptr[0]) ProvideNetInt = true; if (strstr (CgiFormWhatPtr, "PEAK_NET_INT") != NULL) ProvidePeakNetInt = true; cptr = CgiVar ("WWW_FORM_PEAK_NET_INT"); if (cptr[0]) ProvidePeakNetInt = true; if (strstr (CgiFormWhatPtr, "FAULTS") != NULL) ProvidePageFaults = true; cptr = CgiVar ("WWW_FORM_FAULTS"); if (cptr[0]) ProvidePageFaults = true; if (strstr (CgiFormWhatPtr, "PEAK_FAULTS") != NULL) ProvidePeakHardPageFaults = true; cptr = CgiVar ("WWW_FORM_PEAK_FAULTS"); if (cptr[0]) ProvidePeakPageFaults = true; if (strstr (CgiFormWhatPtr, "HARD_FAULTS") != NULL) ProvideHardPageFaults = true; cptr = CgiVar ("WWW_FORM_HARD_FAULTS"); if (cptr[0]) ProvideHardPageFaults = true; if (strstr (CgiFormWhatPtr, "PEAK_HARD_FAULTS") != NULL) ProvidePeakHardPageFaults = true; cptr = CgiVar ("WWW_FORM_PEAK_HARD_FAULTS"); if (cptr[0]) ProvidePeakHardPageFaults = true; if (strstr (CgiFormWhatPtr, "MEMORY") != NULL) ProvideMemory = true; cptr = CgiVar ("WWW_FORM_MEMORY"); if (cptr[0]) ProvideMemory = true; if (strstr (CgiFormWhatPtr, "PROCESSES") != NULL) ProvideProcesses = true; cptr = CgiVar ("WWW_FORM_PROCESSES"); if (cptr[0]) ProvideProcesses = true; CgiFormIncludePtr = CgiVar ("WWW_FORM_INCLUDE"); for (cptr = CgiFormIncludePtr; *cptr; cptr++) *cptr = toupper(*cptr); if (strstr (CgiFormIncludePtr, "PEAK") != NULL) IncludePeak = true; if (strstr (CgiFormIncludePtr, "TOTAL") != NULL) IncludeTotal = true; cptr = CgiVar ("WWW_FORM_FROM"); sscanf (cptr, "%4d-%2d-%2d %2d:%2d", &FromYear, &FromMonth, &FromDay, &FromHour, &FromMinute); cptr = CgiVar ("WWW_FORM_TO"); sscanf (cptr, "%4d-%2d-%2d %2d:%2d", &ToYear, &ToMonth, &ToDay, &ToHour, &ToMinute); CgiFormDoPtr = CgiVar ("WWW_FORM_DO"); CgiFormNodePtr = CgiVar ("WWW_FORM_NODE"); if (!CgiFormNodePtr[0]) CgiFormNodePtr = CgiVar ("WWW_FORM_LIST_NODE"); if (!CgiFormNodePtr[0]) CgiFormNodePtr = SystemNodeName(); if (CgiFormNodePtr[0]) { /* ensure the node name is in upper case */ for (cptr = CgiFormNodePtr; *cptr; cptr++) *cptr = toupper(*cptr); } cptr = CgiVar ("WWW_FORM_EXTRACT"); if (cptr[0]) ExtractGraph = true; CgiFormPeriodPtr = CgiVar ("WWW_FORM_PERIOD"); cptr = CgiVar ("WWW_FORM_XMAG"); if (isdigit(cptr[0])) XMag = atoi (cptr); else XMag = 1; if (XMag > 4) XMag = 4; if (XMag < 1) XMag = 1; cptr = CgiVar ("WWW_FORM_YMAG"); if (isdigit(cptr[0])) YMag = atoi (cptr); else YMag = 1; if (YMag > 4) YMag = 4; if (YMag < 1) YMag = 1; } /*****************************************************************************/ /* Process the request parameters (e.g. time, node name). Verify the parameters are within constraints, particular time. Process the time components into values the application can use. */ ValidateRequest () { static long LibJulianDate = LIB$K_JULIAN_DATE; static char *MonthName [] = { "???", "JAN", "FEB", "MAR", "APR", "MAY", "JUN", "JUL", "AUG", "SEP", "OCT", "NOV", "DEC" }; int status; unsigned long BinTime [2], DeltaTime [2]; unsigned short NumTime [7]; char Scratch [256]; $DESCRIPTOR (TempDsc, ""); /*********/ /* begin */ /*********/ if (Debug) fprintf (stdout, "ValidateRequest()\n"); if (!CgiFormDoPtr[0]) DoSummaryMenu = true; else if (toupper(CgiFormDoPtr[0]) == 'G') DoGraph = true; else if (toupper(CgiFormDoPtr[0]) == 'P') DoPresentSummaryPage = true; else if (toupper(CgiFormDoPtr[0]) == 'C') DoComprehensiveMenu = true; else if (toupper(CgiFormDoPtr[0]) == 'M') DoSummaryMenu = true; else if (toupper(CgiFormDoPtr[0]) == 'D') DoDump = true; else if (toupper(CgiFormDoPtr[0]) == 'L') DoList = true; else { sprintf (Scratch, "Cannot do \"%s\"", CgiFormDoPtr); CgiLibResponseError (FI_LI, 0, Scratch); exit (SS$_NORMAL); } if ((DoPresentSummaryPage || DoGraph || DoList || DoDump) && !CgiFormNodePtr[0]) { CgiLibResponseError (FI_LI, 0, "Node name not specified."); exit (SS$_NORMAL); } /********************/ /* standard periods */ /********************/ if (CgiFormPeriodPtr[0] && toupper(CgiFormPeriodPtr[0]) != 'O') { /* a standard time period has been specified, other than "other" */ FromHour = 0; FromMinute = 0; ToHour = 23; ToMinute = 59; FromDay = ToDay = CurrentNumTime[2]; FromMonth = ToMonth = CurrentNumTime[1]; FromYear = ToYear = CurrentNumTime[0]; if (toupper(CgiFormPeriodPtr[0]) == 'U') { /* "until_now" (7am to this hour) */ FromHour = 7; ToHour = CurrentNumTime[3]; } else if (toupper(CgiFormPeriodPtr[0]) == 'B') { /* "business" hours (7am to 7pm) */ FromHour = 7; ToHour = 18; } else if (toupper(CgiFormPeriodPtr[0]) == 'T') { /* "today" */ ToHour = 23; } else if (toupper(CgiFormPeriodPtr[0]) == 'S' || toupper(CgiFormPeriodPtr[0]) == 'Y') { /* "since_yesterday" until now, or all of "yesterday" */ TempDsc.dsc$a_pointer = "1 00:00:00.00"; TempDsc.dsc$w_length = 13; if (VMSnok (status = sys$bintim (&TempDsc, &DeltaTime))) { CgiLibResponseError (FI_LI, status, CgiFormPeriodPtr); exit (SS$_NORMAL); } lib$sub_times (&CurrentBinTime, &DeltaTime, &BinTime); sys$numtim (&NumTime, &BinTime); FromDay = NumTime[2]; FromMonth = NumTime[1]; FromYear = NumTime[0]; if (toupper(CgiFormPeriodPtr[0]) == 'S') ToHour = CurrentNumTime[3]; else { ToDay = NumTime[2]; ToMonth = NumTime[1]; ToYear = NumTime[0]; } } else if (isdigit(CgiFormPeriodPtr[0])) { TempDsc.dsc$a_pointer = Scratch; TempDsc.dsc$w_length = sprintf (Scratch, "%s 00:00:00.00", CgiFormPeriodPtr); if (VMSnok (status = sys$bintim (&TempDsc, &DeltaTime))) { CgiLibResponseError (FI_LI, status, CgiFormPeriodPtr); exit (SS$_NORMAL); } lib$sub_times (&CurrentBinTime, &DeltaTime, &BinTime); sys$numtim (&NumTime, &BinTime); FromDay = NumTime[2]; FromMonth = NumTime[1]; FromYear = NumTime[0]; } else { CgiLibResponseError (FI_LI, 0, "Periods are \"until_now\""); exit (SS$_NORMAL); } } /*******************/ /* time components */ /*******************/ /* non-specified date components default to those of the current day */ if (!FromDay) FromDay = CurrentNumTime[2]; if (!FromMonth) FromMonth = CurrentNumTime[1]; if (!FromYear) FromYear = CurrentNumTime[0]; if (!ToDay) ToDay = CurrentNumTime[2]; if (!ToMonth) ToMonth = CurrentNumTime[1]; if (!ToYear) ToYear = CurrentNumTime[0]; /* bit of a sanity check (prevents 'MonthName[x]' access violating, etc.) */ if (FromMonth < 1 || FromMonth > 12) FromMonth = 0; if (ToMonth < 1 || ToMonth > 12) ToMonth = 0; /* ensure the commencement date/time is acceptable */ TempDsc.dsc$a_pointer = DateString; TempDsc.dsc$w_length = sprintf (DateString, "%d-%s-%d %02.02d:%02.02d", FromDay, MonthName[FromMonth], FromYear, FromHour, FromMinute); if (Debug) fprintf (stdout, "DateString |%s|\n", DateString); if (VMSnok (status = sys$bintim (&TempDsc, &BinTime))) { sprintf (Scratch, "%02.02d:%02.02d %02.02d/%02.02d/%02.02d", FromHour, FromMinute, FromDay, FromMonth, FromYear); CgiLibResponseError (FI_LI, status, Scratch); exit (SS$_NORMAL); } /* get the commencement julian date (number of days in epoch) */ lib$cvt_from_internal_time (&LibJulianDate, &FromJulianDate, &BinTime); /* ensure the conclusion date/time is acceptable */ TempDsc.dsc$a_pointer = ToDateString; TempDsc.dsc$w_length = sprintf (ToDateString, "%d-%s-%d %02.02d:%02.02d", ToDay, MonthName[ToMonth], ToYear, ToHour, ToMinute); if (Debug) fprintf (stdout, "ToDateString |%s|\n", ToDateString); if (VMSnok (status = sys$bintim (&TempDsc, &BinTime))) { sprintf (Scratch, "%02.02d:%02.02d %02.02d/%02.02d/%02.02d", ToHour, ToMinute, ToDay, ToMonth, ToYear); CgiLibResponseError (FI_LI, status, Scratch); exit (SS$_NORMAL); } /* get the conclusion julian date (number of days in epoch) */ lib$cvt_from_internal_time (&LibJulianDate, &ToJulianDate, &BinTime); /***************************/ /* calculate period values */ /***************************/ /* using julian dates, get number of days and hours sample period covers */ NumberOfHours = ((ToJulianDate - FromJulianDate) * 24) + ToHour - FromHour; NumberOfDays = (NumberOfHours / 24); if (DoPresentSummaryPage || DoGraph || DoList || DoDump) { if (NumberOfHours < 0 || (NumberOfHours == 0 && ToMinute <= FromMinute)) { sprintf (Scratch, "Beginning of period (%04.04d-%02.02d-%02.02d %02.02d:%02.02d) \ does not precede end (%04.04d-%02.02d-%02.02d %02.02d:%02.02d)!", FromYear, FromMonth, FromDay, FromHour, FromMinute, ToYear, ToMonth, ToDay, ToHour, ToMinute); CgiLibResponseError (FI_LI, 0, Scratch); exit (SS$_NORMAL); } if (NumberOfHours < 1) { CgiLibResponseError (FI_LI, 0, "Little point to such a short period!"); exit (SS$_NORMAL); } if (NumberOfDays > MAX_SAMPLE_PERIOD_DAYS) { sprintf (Scratch, "Maximum sample period is %d days.", MAX_SAMPLE_PERIOD_DAYS); CgiLibResponseError (FI_LI, 0, Scratch); exit (SS$_NORMAL); } } /* rate at which the data (and any graphic) must be X-axis compressed */ if (NumberOfHours <= 12) RecordSampleRate = 1; else if (NumberOfHours <= 24) RecordSampleRate = 2; else RecordSampleRate = (NumberOfDays + 1) * 2; /* add one to number of hours, for graphing purposes, re-calculate days */ NumberOfHours++; NumberOfDays = (NumberOfHours / 24); /* the number of minutes from midnight data begins being processed */ StartMinuteOnFirstDay = (FromHour * 60) + FromMinute; /* the current minute from the start of the specified period */ lib$cvt_from_internal_time (&LibJulianDate, &CurrentJulianDate, &CurrentBinTime); CurrentMinuteFromStartOfPeriod = ((((CurrentJulianDate - FromJulianDate) * 24) + (CurrentNumTime[3] - FromHour)) * 60) + CurrentNumTime[4]; if (Debug) fprintf (stdout, "FromJulianDate: %d ToJulianDate: %d\n\ NumberOfDays: %d NumberOfHours: %d RecordSampleRate: %d\n", FromJulianDate, ToJulianDate, NumberOfDays, NumberOfHours, RecordSampleRate); } /*****************************************************************************/ /* Provide either a standard or comprehensive menu allowing System Performance Information system, category and period to be specified. */ SummaryMenu () { char *CheckedPtr, *TodayCheckedPtr, *UntilNowCheckedPtr; /*********/ /* begin */ /*********/ if (Debug) fprintf (stdout, "SummaryMenu()\n"); if (DoComprehensiveMenu) CheckedPtr = ""; else CheckedPtr = " checked"; CgiLibResponseHeader (200, "text/html"); fprintf (stdout, "<!DOCTYPE html>\n\ <html>\n\ <head>\n\ <meta http-equiv=\"X-UA-Compatible\" content=\"IE=edge\">\n\ <meta name=\"generator\" content=\"%s\">\n\ <meta name=\"copyright\" content=\"%s - GPL licensed\">\n\ <meta name=\"environment\" content=\"%s\">\n\ <meta name=\"date\" content=\"%s\">\n\ <title>HyperSPI, Summary Selector</title>\n\ <style type=\"text/css\">\n\ %s\ </style>\n\ %s\ </head>\n\ <body>\n", SoftwareID, SoftwareCopy, CgiEnvironmentPtr, UnixDateTime, DefaultStyle, StyleSheetPtr); fprintf (stdout, "<div class=\"header\">\ <div class=\"hyperSPI\">HyperSPI</div>\ <div class=\"heading\">Summary Selector</div>\ <div class=\"datetime\">%d %s %d %0.2d:%0.2d</div>\ </div>\n", CurrentNumTime[2], MonthName[CurrentNumTime[1]], CurrentNumTime[0], CurrentNumTime[3], CurrentNumTime[4]); HttpHasBeenOutput = true; fprintf (stdout, "<div class=\"content\">\n\ <form action=\"%s\">\n", CgiScriptNamePtr); SelectNodeNames (); if (DoComprehensiveMenu) { fputs ( " or ... node \ <input type=\"text\" name=\"node\" size=\"6\" maxlength=\"6\"><br>", stdout); } if (CurrentNumTime[3] >= 7) { UntilNowCheckedPtr = " checked"; TodayCheckedPtr = ""; } else { UntilNowCheckedPtr = ""; TodayCheckedPtr = " checked"; } fprintf (stdout, "<p>\n\ <input type=\"radio\" name=\"period\" value=\"until_now\"%s>\ since 7am <font size=-1>(until now)</font><br>\ <input type=\"radio\" name=\"period\" value=\"today\"%s> today<br>\n\ <input type=\"radio\" name=\"period\" value=\"since_yesterday\">\ since yesterday <font size=-1>(until now)</font><br>\n\ <input type=\"radio\" name=\"period\" value=\"yesterday\"> yesterday<br>\n\ <input type=\"radio\" name=\"period\" value=\"7\"> previous week \n\ <input type=\"radio\" name=\"period\" value=\"14\"> two weeks \n\ <input type=\"radio\" name=\"period\" value=\"28\"> four weeks<br>\n\ <nobr>\n\ <input type=radio name=period value=other>\ from\n\ <input type=\"text\" size=\"16\" maxlength=\"16\" name=\"from\" \ value=\"%04.04d-%02.02d-%02.02d %02.02d:00\">\ to\n\ <input type=\"text\" size=\"16\" maxlength=\"16\" name=\"to\" \ value=\"%04.04d-%02.02d-%02.02d 23:59\">\ <font size=-1>(yyyy-mm-dd hh:mm)</font>\n\ </nobr>\n\ <p>\n\ <input type=\"checkbox\" name=\"cpu\" value=\"yes\" checked> cpu<br>\n\ <input type=\"checkbox\" name=\"memory\" value=\"yes\"%s> memory<br>\n\ <input type=\"checkbox\" name=\"processes\" value=\"yes\"> processes<br>\n", UntilNowCheckedPtr, TodayCheckedPtr, CurrentNumTime[0], CurrentNumTime[1], CurrentNumTime[2], CurrentNumTime[3], CurrentNumTime[0], CurrentNumTime[1], CurrentNumTime[2], CheckedPtr); if (DoComprehensiveMenu) { fputs ( "<input type=\"checkbox\" name=\"faults\" value=\"yes\"> paging (soft) \ <input type=\"checkbox\" name=\"peak_faults\" value=\"yes\"> peak<br>\n", stdout); } fprintf (stdout, "<input type=\"checkbox\" name=\"hard_faults\" value=\"yes\"%s> paging", CheckedPtr); if (DoComprehensiveMenu) { fputs ( " (hard)\ <input type=\"checkbox\" name=\"peak_hard_faults\" value=\"yes\"> peak", stdout); } fputs ("<br>\n", stdout); fputs ( "<input type=\"checkbox\" name=\"direct_IO\" value=\"yes\"> disk IO", stdout); if (DoComprehensiveMenu) { fputs ( " <input type=\"checkbox\" name=\"peak_direct_IO\" value=\"yes\"> peak", stdout); } fputs ("<br>\n", stdout); fputs ( "<input type=\"checkbox\" name=\"buffered_IO\" value=\"yes\"> other IO", stdout); if (DoComprehensiveMenu) { fputs ( " <input type=\"checkbox\" name=\"peak_buffered_IO\" value=\"yes\"> peak", stdout); } fputs ("<br>\n", stdout); fputs ( "<input type=\"checkbox\" name=\"mscp_IO\" value=\"yes\"> mscp IO", stdout); if (DoComprehensiveMenu) { fputs ( " <input type=\"checkbox\" name=\"peak_mscp_IO\" value=\"yes\"> peak", stdout); } fputs ("<br>\n", stdout); fputs ( "<input type=\"checkbox\" name=\"net_int\" value=\"yes\"> network", stdout); if (DoComprehensiveMenu) { fputs ( " <input type=\"checkbox\" name=\"peak_net_int\" value=\"yes\"> peak", stdout); } fputs ("<br>\n", stdout); if (DoComprehensiveMenu) { fputs ( "<input type=\"checkbox\" name=\"include\" value=\"peak\">\ <i>include peak plot (where applicable)</i>\n\ <p>\n\ <input type=\"radio\" name=\"do\" value=\"page\" checked>graph ...\ X,Y magnification:\ <select name=\"Xmag\" size=\"1\">\n\ <option value=\"1\" selected>1 \n\ <option value=\"2\">2 \n\ <option value=\"4\">4 \n\ </select>,\ <select name=\"Ymag\" size=\"1\">\n\ <option value=\"1\" selected> 1 \n\ <option value=\"2\"> 2 \n\ <option value=\"4\"> 4 \n\ </select><br>\n\ <input type=\"radio\" name=\"do\" value=\"list\"> list<br>\n\ <input type=\"radio\" name=\"do\" value=\"dump\"> dump<br>\n\ <input type=\"hidden\" name=\"extract\" value=\"yes\">\n\ <p>\n", stdout); } else { fputs ( "<input type=\"hidden\" name=\"do\" value=\"page\">\n", stdout); } fputs ( "<p><select name=\"refresh\" size=\"1\">\n\ <option value=\"0\" selected>no\n\ <option value=\"5\">5\n\ <option value=\"10\">10\n\ <option value=\"15\">15\n\ <option value=\"30\">30\n\ <option value=\"60\">60\n\ </select> refresh (minutes)", stdout); fputs ( "<p>\n\ <input type=\"submit\" value=\"Process\">\n", stdout); if (!DoComprehensiveMenu) { fputs ( " <input type=\"checkbox\" name=\"ymag\" value=\"2\"> \ double-height graph \n", stdout); } fprintf (stdout, "<input type=\"reset\" value=\"Reset\">\n\ </form>\n\ </div>\n"); SelectorPtr = NULL; ButtonBar (2); fprintf (stdout, "</body>\n</html>\n"); } /*****************************************************************************/ /* Output a list of node names that data can be selected from. Get these either from a command-line-supplied list or from data files present for the current day. */ SelectNodeNames () { int idx; char *CheckedPtr; /*********/ /* begin */ /*********/ if (Debug) fprintf (stdout, "SelectNodeNames()\n"); if (SpiNodeNames[0][0]) { NodeCount = 0; for (idx = 0; SpiNodeNames[idx][0]; idx++) NodeCount++; if (NodeCount <= MAX_NODE_COUNT_BEFORE_SELECT) { CheckedPtr = " checked"; for (idx = 0; SpiNodeNames[idx][0]; idx++) { fprintf (stdout, "<input type=\"radio\" name=\"list_node\" value=\"%s\"%s> %s<br>\n", SpiNodeNames[idx], CheckedPtr, SpiNodeNames[idx]); CheckedPtr = ""; } } else { CheckedPtr = " selected"; fprintf (stdout, "<select name=\"list_node\" size=\"%d\">\n", MAX_NODE_COUNT_BEFORE_SELECT); for (idx = 0; SpiNodeNames[idx][0]; idx++) { fprintf (stdout, "<option value=\"%s\"%s> %s\n", SpiNodeNames[idx], CheckedPtr, SpiNodeNames[idx]); CheckedPtr = ""; } fputs ("</SELECT>\n", stdout); } } else { /* create file specification for getting node names (today's files) */ DataFileSpecLength = sprintf (DataFileSpec, "%sHYPERSPI_%s_*_%02.02d%02.02d%02.02d.DAT", HYPERSPI_DATA_DIRECTORY, HYPERSPI_DATA_VERSION, FromDay, FromMonth, FromYear%100); if (Debug) fprintf (stdout, "DataFileSpec |%s|\n", DataFileSpec); /* count the number of node data files */ ProcessDataFiles (NULL, false); NodeCount = DataFilesFoundCount; if (NodeCount <= MAX_NODE_COUNT_BEFORE_SELECT) ProcessDataFiles (&SelectNodeNameByDataFileName, false); else { fprintf (stdout, "<SELECT NAME=list_node SIZE=%d>\n", MAX_NODE_COUNT_BEFORE_SELECT); ProcessDataFiles (&SelectNodeNameByDataFileName, false); fputs ("</SELECT>\n", stdout); } } } /*****************************************************************************/ /* Called by pointer to function each time a matching data file is returned by sys$search(0 in function FindDataFiles(). Output the node name associated with the data file. */ SelectNodeNameByDataFileName () { static char *CheckedPtr = " checked"; static char *SelectedPtr = " selected"; /*********/ /* begin */ /*********/ if (Debug) fprintf (stdout, "SelectNodeNameByFileName() |%s|\n", DataFileName); if (NodeCount < MAX_NODE_COUNT_BEFORE_SELECT) { fprintf (stdout, "<input type=\"radio\" name=\"list_node\" value=\"%s\"%s> %s<br>\n", DataNode, CheckedPtr, DataNode); CheckedPtr = ""; } else { fprintf (stdout, "<option value=\"%s\"%s> %s\n", DataNode, SelectedPtr, DataNode); SelectedPtr = ""; } } /*****************************************************************************/ /* Generates an HTML page containing information about the selected node, and the any additional performance information and graphs. */ PresentSummaryPage () { int PeriodHours; char MetaRefresh [64]; /*********/ /* begin */ /*********/ if (Debug) fprintf (stdout, "PresentSummaryPage()\n"); if (RefreshSeconds) sprintf (MetaRefresh, "<meta http-equiv=\"refresh\" content=\"%d\">\n", RefreshSeconds); else MetaRefresh[0] = '\0'; CgiLibResponseHeader (200, "text/html"); fprintf (stdout, "<!DOCTYPE html>\n\ <html>\n\ <head>\n\ <meta http-equiv=\"X-UA-Compatible\" content=\"IE=edge\">\n\ <meta name=\"generator\" content=\"%s\">\n\ <meta name=\"copyright\" content=\"%s - GPL licensed\">\n\ <meta name=\"environment\" content=\"%s\">\n\ %s\ <title>HyperSPI - Data Summary - %s</title>\n\ <style type=\"text/css\">\n\ %s\ </style>\n\ %s\ </head>\n\ <body>\n", SoftwareID, SoftwareCopy, CgiEnvironmentPtr, MetaRefresh, CgiFormNodePtr, DefaultStyle, StyleSheetPtr); fprintf (stdout, "<div class=\"header\">\ <div class=\"hyperSPI\">HyperSPI</div>\ <div class=\"heading\">Summary for %s</div>\ <div class=\"datetime\">%d %s %d %0.2d:%0.2d</div>\ </div>\n", CgiFormNodePtr, CurrentNumTime[2], MonthName[CurrentNumTime[1]], CurrentNumTime[0], CurrentNumTime[3], CurrentNumTime[4]); fprintf (stdout, "<div class=\"content\">\n"); fflush (stdout); HttpHasBeenOutput = true; fprintf (stdout, "<table class=\"precis\">\n<tr><th>Period:</th><td>"); if ((!NumberOfDays && NumberOfHours == 24) || NumberOfDays == 1) fputs ("1 day", stdout); else if (NumberOfDays > 1) fprintf (stdout, "%d days", NumberOfDays); if ((PeriodHours = (NumberOfHours % 24)) == 1) fputs ("1 hour", stdout); else if (PeriodHours > 1) fprintf (stdout, " %d hours", PeriodHours); fprintf (stdout, ", from %02.02d %s %d %02.02d:%02.02d to %02.02d %s %d %02.02d:%02.02d\ </td></tr>\n", FromDay, MonthName[FromMonth], FromYear, FromHour, FromMinute, ToDay, MonthName[ToMonth], ToYear, ToHour, ToMinute); /* summarize the data and calculate required averages */ SummarizeData (); if (DataRecordsProcessedCount) { IncludeFile (CgiFormNodePtr); fprintf (stdout, "<tr><th>CPUs:</th><td>%d</td></tr>\n\ <tr><th>Memory:</th><td>%dMB physical, %dMB page space \ (%dmb total)</td></tr>\n\ <tr><th>Processes:</th><td>%d average, %d peak</td></tr>\n\ <tr><td colspan=\"2\" style=\"font-size:80%%;\">\ (x axes are marked at hour intervals)</td></tr>\n\ </table>\n\ <p>\n", NumberOfCPUs, SystemMemoryMBytes, PageSpaceMBytes, SystemMemoryMBytes+PageSpaceMBytes, AveNumberOfProcesses, MaxNumberOfProcesses); if (ProvidePercentCPU) PresentCPU (); if (ProvideMemory) PresentMemory (); if (ProvideProcesses) PresentProcesses (); if (ProvidePageFaults) PresentPageFaults (); if (ProvidePeakPageFaults) PresentPeakPageFaults (); if (ProvideHardPageFaults) PresentHardPageFaults (); if (ProvidePeakHardPageFaults) PresentPeakHardPageFaults (); if (ProvideBufferedIO) PresentBufferedIO (); if (ProvidePeakBufferedIO) PresentPeakBufferedIO (); if (ProvideDirectIO) PresentDirectIO (); if (ProvidePeakDirectIO) PresentPeakDirectIO (); if (ProvideMscpIO) PresentMscpIO (); if (ProvidePeakMscpIO) PresentPeakMscpIO (); if (ProvideNetInt) PresentNetInt (); if (ProvidePeakNetInt) PresentPeakNetInt (); } else fputs ("No data available.\n", stdout); fprintf (stdout, "</div>\n"); ButtonBar (2); fprintf (stdout, "</body>\n</html>\n"); } /*****************************************************************************/ /* Place an HTML link into the HTTP output stream to get a plot-data, GIF image, generated dynamically by this application, embedded in the current HTML document. The 'UniqueNumber()' is used to place a unique component into the URL of the graph GIF URL, ensuring a cached version is not retrieved. The 'WhatPtr' is the name of the performance count to graph. The optional 'IncludePtr' allows selected graphs to contain additional information. */ GraphImageLink ( boolean InlineImage, char *WhatPtr, char *IncludePtr, ... ) { #define LABEL_UNITS 0 #define LABEL_Y1 1 #define LABEL_Y2 2 #define LABEL_X1 3 #define LABEL_X2 4 int argcnt, idx; char *Labels[5] = { NULL,NULL,NULL,NULL,NULL }; va_list argptr; /*********/ /* begin */ /*********/ if (Debug) fprintf (stdout, "GraphImageLink()\n"); va_count (argcnt); if (argcnt < 3 || argcnt > 8) exit (SS$_BUGCHECK); /* labels: Y units, Y axis 1, Y axis 2, X axis 1, X axis 2 */ idx = 0; va_start (argptr, IncludePtr); for (argcnt -= 3; argcnt; argcnt--) Labels[idx++] = (char*)va_arg (argptr, char*); va_end (argptr); for (idx = 0; idx < 5; idx++) if (!Labels[idx]) Labels[idx] = ""; if (InlineImage) { fprintf (stdout, "<table class=\"graph\" cellpadding=\"0\" cellspacing=\"0\" border=\"0\">\n\ <tr><td style=\"width:4em;font-size:80%%;\ text-align:right;vertical-align:top;\">%s</td><td rowspan=\"3\">\n\ <img align=\"top\" alt=\"[graph]\" src=\"", Labels[LABEL_Y1]); } else fputs ("<a class=\"extrbtn\" href=\"", stdout); fprintf (stdout, "%s?do=graph&unique=%s&%s=yes&include=%s\ &Xmag=%d&Ymag=%d&node=%s\ &from=%04.04d-%02.02d-%02.02d%%20%02.02d:%02.02d\ &to=%04.04d-%02.02d-%02.02d%%20%02.02d:%02.02d", CgiScriptNamePtr, UniqueNumber(), WhatPtr, IncludePtr, XMag, YMag, CgiFormNodePtr, FromYear, FromMonth, FromDay, FromHour, FromMinute, ToYear, ToMonth, ToDay, ToHour, ToMinute); if (InlineImage) { fprintf (stdout, "\">\n\ </td><td style=\"font-size:80%%;\ text-align:right;vertical-align:top;\">%s</td></tr>\n\ <tr><td style=\"font-size:80%%;padding:5px 15px 0 0;white-space:nowrap;\ text-align:right;vertical-align:middle;\">%s</td></tr>\n\ <tr><td style=\"font-size:80%%;padding-bottom:10px;\ text-align:right;vertical-align:bottom;\">%s</td>\ <td style=\"font-size:80%%;padding-bottom:10px;\ text-align:left;vertical-align:bottom;\">%s</td></tr>\n\ </table>\n", Labels[LABEL_Y2], Labels[LABEL_UNITS], Labels[LABEL_X1], Labels[LABEL_X2]); } else fputs ("\">Extract Graph</a>\n", stdout); } /*****************************************************************************/ /* CPU usage. Place textual information and a graphic GIF image link into the current HTML document being generated. */ PresentCPU () { char NumCPU [32]; /*********/ /* begin */ /*********/ if (Debug) fprintf (stdout, "PresentCPU()\n"); fprintf (stdout, "<div class=\"graphtitle\">CPU Usage</div>\n\ <div class=\"graphdata\">\n"); sprintf (NumCPU, "%d CPU%s", NumberOfCPUs, NumberOfCPUs == 1 ? "" : "s"); GraphImageLink (true, "cpu", "peak", NumCPU, "100%", NULL, "0"); fprintf (stdout, "%d%% average, %d%% peak; user-mode %d%% average, %d%% peak.\n", AvePercentCPU, PeakPercentCPU, AvePercentUserModeCPU, PeakPercentUserModeCPU); if (ExtractGraph) GraphImageLink (false, "cpu", "peak", NumCPU, "100%", NULL, "0"); fprintf (stdout, "</div>\n"); } /*****************************************************************************/ /* Page space and physical memory usage. Place textual information and a graphic GIF image link into the current HTML document being generated. */ PresentMemory () { char Yaxis1 [32], Yaxis2 [32]; /*********/ /* begin */ /*********/ if (Debug) fprintf (stdout, "PresentMemory()\n"); fprintf (stdout, "<div class=\"graphtitle\">Memory Usage</div>\n\ <div class=\"graphdata\">\n"); sprintf (Yaxis1, "%dMB", SystemMemoryMBytes); sprintf (Yaxis2, "%dMB<br>(pgfl)", PageSpaceMBytes); GraphImageLink (true, "memory", "", NULL, Yaxis1, Yaxis2, "0", "0"); fprintf (stdout, "Physical memory %d%% average, %d%% peak; \ page space %d%% average, %d%% peak.\n", AveSystemMemoryPercentInUse, MaxSystemMemoryPercentInUse, AvePageSpacePercentInUse, MaxPageSpacePercentInUse); if (ExtractGraph) GraphImageLink (false, "memory", "", NULL, Yaxis1, Yaxis2, 0); fprintf (stdout, "</div>\n"); } /*****************************************************************************/ /* Number of processes. Place textual information and a graphic GIF image link into the current HTML document being generated. */ PresentProcesses () { char Yaxis [32]; /*********/ /* begin */ /*********/ if (Debug) fprintf (stdout, "PresentProcesses()\n"); fprintf (stdout, "<div class=\"graphtitle\">Number of Processes</div>\n\ <div class=\"graphdata\">\n"); sprintf (Yaxis, "%d", ProvideScaling(PresentProcesses)); GraphImageLink (true, "processes", "", NULL, Yaxis, NULL, "0"); fprintf (stdout, "%d average, maximum %d.\n", AveNumberOfProcesses, MaxNumberOfProcesses); if (ExtractGraph) GraphImageLink (false, "processes", "", NULL, Yaxis, NULL, "0"); fprintf (stdout, "</div>\n"); } /*****************************************************************************/ /* Buffered IO (non-disk, non-tape, i.e. network, terminal). Place textual information and a graphic GIF image link into the current HTML document being generated. */ PresentBufferedIO () { char Yaxis [32]; /*********/ /* begin */ /*********/ if (Debug) fprintf (stdout, "PresentBufferedIO()\n"); fprintf (stdout, "<div class=\"graphtitle\">Buffered IO (network, terminal, etc.)</div>\n\ <div class=\"graphdata\">\n"); sprintf (Yaxis, "%d", ProvideScaling(PresentBufferedIO)); if (IncludePeak) GraphImageLink (true, "buffered_IO", "peak", "IO/S", Yaxis, NULL, "0"); else GraphImageLink (true, "buffered_IO", "", "IO/S", Yaxis, NULL, "0"); fprintf (stdout, "%d average, maximum %d, peak %d.\n", AveBufferedIO, MaxAveBufferedIO, PeakBufferedIO); if (IncludePeak) if (ExtractGraph) GraphImageLink (false, "buffered_IO", "peak", "IO/S", Yaxis, NULL, "0"); else; else if (ExtractGraph) GraphImageLink (false, "buffered_IO", "", "IO/S", Yaxis, NULL, "0"); fprintf (stdout, "</div>\n"); } /*****************************************************************************/ /* Peak buffered IO. Place textual information and a graphic GIF image link into the current HTML document being generated. */ PresentPeakBufferedIO () { char Yaxis [32]; /*********/ /* begin */ /*********/ if (Debug) fprintf (stdout, "PresentPeakBufferedIO()\n"); fprintf (stdout, "<div class=\"graphtitle\">Peak Buffered IO (network, terminal, etc.)</div>\n\ <div class=\"graphdata\">\n"); sprintf (Yaxis, "%d", ProvideScaling(PresentPeakBufferedIO)); GraphImageLink (true, "peak_buffered_IO", "", "IO/S", Yaxis, NULL, "0"); fprintf (stdout, "Peak %d per-second.\n", PeakBufferedIO); if (ExtractGraph) GraphImageLink (false, "peak_buffered_IO", "", "IO/S", Yaxis, NULL, "0"); fprintf (stdout, "</div>\n"); } /*****************************************************************************/ /* Direct IO (disk, tape, etc). Place textual information and a graphic GIF image link into the current HTML document being generated. */ PresentDirectIO () { char Yaxis [32]; /*********/ /* begin */ /*********/ if (Debug) fprintf (stdout, "PresentDirectIO()\n"); fprintf (stdout, "<div class=\"graphtitle\">Direct IO (disk, tape, etc.)</div>\n\ <div class=\"graphdata\">\n"); sprintf (Yaxis, "%d", ProvideScaling(PresentDirectIO)); if (IncludePeak) GraphImageLink (true, "direct_IO", "peak", "IO/S", Yaxis, NULL, "0"); else GraphImageLink (true, "direct_IO", "", "IO/S", Yaxis, NULL, "0"); fprintf (stdout, "%d average, maximum %d, peak %d.\n", AveDirectIO, MaxAveDirectIO, PeakDirectIO); if (IncludePeak) if (ExtractGraph) GraphImageLink (false, "direct_IO", "peak", "IO/S", Yaxis, NULL, "0"); else; else if (ExtractGraph) GraphImageLink (false, "direct_IO", "", "IO/S", Yaxis, NULL, "0"); fprintf (stdout, "</div>\n"); } /*****************************************************************************/ /* Peak direct IO. Place textual information and a graphic GIF image link into the current HTML document being generated. */ PresentPeakDirectIO () { char Yaxis [32]; /*********/ /* begin */ /*********/ if (Debug) fprintf (stdout, "PresentPeakDirectIO()\n"); fprintf (stdout, "<div class=\"graphtitle\">Peak Direct IO (disk, tape, etc.)</div>\n\ <div class=\"graphdata\">\n"); sprintf (Yaxis, "%d", ProvideScaling(PresentPeakDirectIO)); GraphImageLink (true, "peak_direct_IO", "", "IO/S", Yaxis, NULL, "0"); fprintf (stdout, "Peak %d per-second.\n", PeakDirectIO); if (ExtractGraph) GraphImageLink (false, "peak_direct_IO", "", "IO/S", Yaxis, NULL, "0"); fprintf (stdout, "</div>\n"); } /*****************************************************************************/ /* MSCP IO. Place textual information and a graphic GIF image link into the current HTML document being generated. */ PresentMscpIO () { char Yaxis [32]; /*********/ /* begin */ /*********/ if (Debug) fprintf (stdout, "PresentMscpIO()\n"); fprintf (stdout, "<div class=\"graphtitle\">MSCP IO (served disk and tape)</div>\n\ <div class=\"graphdata\">\n"); sprintf (Yaxis, "%d", ProvideScaling(PresentMscpIO)); if (IncludePeak) GraphImageLink (true, "mscp_IO", "peak", "IO/S", Yaxis, NULL, "0"); else GraphImageLink (true, "mscp_IO", "", "IO/S", Yaxis, NULL, "0"); fprintf (stdout, "%d average, maximum %d, peak %d.\n", AveMscpIO, MaxAveMscpIO, PeakMscpIO); if (IncludePeak) if (ExtractGraph) GraphImageLink (false, "mscp_IO", "peak", "IO/S", Yaxis, NULL, "0"); else; else if (ExtractGraph) GraphImageLink (false, "mscp_IO", "", "IO/S", Yaxis, NULL, "0"); fprintf (stdout, "</div>\n"); } /*****************************************************************************/ /* Peak MSCP IO. Place textual information and a graphic GIF image link into the current HTML document being generated. */ PresentPeakMscpIO () { char Yaxis [32]; /*********/ /* begin */ /*********/ if (Debug) fprintf (stdout, "PresentPeakMscpIO()\n"); fprintf (stdout, "<div class=\"graphtitle\">Peak MSCP IO (served disk and tape)</div>\n\ <div class=\"graphdata\">\n"); sprintf (Yaxis, "%d", ProvideScaling(PresentPeakMscpIO)); GraphImageLink (true, "peak_mscp_IO", "", "IO/S", Yaxis, NULL, "0"); fprintf (stdout, "Peak %d per-second.\n", PeakMscpIO); if (ExtractGraph) GraphImageLink (false, "peak_mscp_IO", "", "IO/S", Yaxis, NULL, "0"); fprintf (stdout, "</div>\n"); } /*****************************************************************************/ /* Network interface traffic. Place textual information and a graphic GIF image link into the current HTML document being generated. */ PresentNetInt () { char Yaxis [32]; /*********/ /* begin */ /*********/ if (Debug) fprintf (stdout, "PresentNetInt()\n"); fprintf (stdout, "<div class=\"graphtitle\">Network Interface</div>\n\ <div class=\"graphdata\">\n"); sprintf (Yaxis, "%d", ProvideScaling(PresentNetInt)); if (IncludePeak) GraphImageLink (true, "net_int", "peak", "kB/S", Yaxis, NULL, "0"); else GraphImageLink (true, "net_int", "", "kB/S", Yaxis, NULL, "0"); fprintf (stdout, "%d average, maximum %d, peak %d.\n", AveNetIntRxTx, MaxAveNetIntRxTx, PeakNetIntRxTx); if (IncludePeak) if (ExtractGraph) GraphImageLink (false, "net_int", "peak", "kB/S", Yaxis, NULL, "0"); else; else if (ExtractGraph) GraphImageLink (false, "net_int", "", "kB/S", Yaxis, NULL, "0"); fprintf (stdout, "</div>\n"); } /*****************************************************************************/ /* Peak mscp IO. Place textual information and a graphic GIF image link into the current HTML document being generated. */ PresentPeakNetInt () { char Yaxis [32]; /*********/ /* begin */ /*********/ if (Debug) fprintf (stdout, "PresentPeakNetInt()\n"); fprintf (stdout, "<div class=\"graphtitle\">Peak Network Interface</div>\n\ <div class=\"graphdata\">\n"); sprintf (Yaxis, "%d", ProvideScaling(PresentPeakNetInt)); GraphImageLink (true, "peak_net_int", "", "kB/S", Yaxis, NULL, "0"); fprintf (stdout, "Peak %d per-second.\n", PeakNetIntRxTx); if (ExtractGraph) GraphImageLink (false, "peak_net_int", "", "kB/S", Yaxis, NULL, "0"); fprintf (stdout, "</div>\n"); } /*****************************************************************************/ /* Page faulting IO. Place textual information and a graphic GIF image link into the current HTML document being generated. */ PresentPageFaults () { char Yaxis [32]; /*********/ /* begin */ /*********/ if (Debug) fprintf (stdout, "PresentPageFaults()\n"); fprintf (stdout, "<div class=\"graphtitle\">Paging (Soft)</div>\n\ <div class=\"graphdata\">\n"); sprintf (Yaxis, "%d", ProvideScaling(PresentPageFaults)); if (IncludePeak) GraphImageLink (true, "faults", "peak", "flt/S", Yaxis, NULL, "0"); else GraphImageLink (true, "faults", "", "flt/S", Yaxis, NULL, "0"); fprintf (stdout, "%d average, maximum %d, peak %d.\n", AvePageFaults, MaxAvePageFaults, PeakPageFaults); if (IncludePeak) if (ExtractGraph) GraphImageLink (false, "faults", "peak", "flt/S", Yaxis, NULL, "0"); else; else if (ExtractGraph) GraphImageLink (false, "faults", "", "flt/S", Yaxis, NULL, "0"); fprintf (stdout, "</div>\n"); } /*****************************************************************************/ /* Peak paging to disk. Place textual information and a graphic GIF image link into the current HTML document being generated. */ PresentPeakPageFaults () { char Yaxis [32]; /*********/ /* begin */ /*********/ if (Debug) fprintf (stdout, "PresentPeakPageFaults()\n"); fprintf (stdout, "<div class=\"graphtitle\">Peak Paging (Soft)</div>\n\ <div class=\"graphdata\">\n"); sprintf (Yaxis, "%d", ProvideScaling(PresentPeakPageFaults)); GraphImageLink (true, "peak_faults", "", "flt/S", Yaxis, NULL, "0"); fprintf (stdout, "BR>Peak %d per-second.\n", PeakPageFaults); if (ExtractGraph) GraphImageLink (false, "peak_faults", "", "flt/S", Yaxis, NULL, "0"); fprintf (stdout, "</div>\n"); } /*****************************************************************************/ /* Page faulting IO. Place textual information and a graphic GIF image link into the current HTML document being generated. */ PresentHardPageFaults () { char Yaxis [32]; /*********/ /* begin */ /*********/ if (Debug) fprintf (stdout, "PresentHardPageFaults()\n"); fprintf (stdout, "<div class=\"graphtitle\">Paging (Hard)</div>\n\ <div class=\"graphdata\">\n"); sprintf (Yaxis, "%d", ProvideScaling(PresentHardPageFaults)); if (IncludePeak) GraphImageLink (true, "hard_faults", "peak", "flt/S", Yaxis, NULL, "0"); else GraphImageLink (true, "hard_faults", "", "flt/S", Yaxis, NULL, "0"); fprintf (stdout, "%d average, maximum %d, peak %d.\n", AveHardPageFaults, MaxAveHardPageFaults, PeakHardPageFaults); if (IncludePeak) if (ExtractGraph) GraphImageLink (false, "hard_faults", "peak", "flt/S", Yaxis, NULL, "0"); else; else if (ExtractGraph) GraphImageLink (false, "hard_faults", "", "flt/S", Yaxis, NULL, "0"); fprintf (stdout, "</div>\n"); } /*****************************************************************************/ /* Peak paging to disk. Place textual information and a graphic GIF image link into the current HTML document being generated. */ PresentPeakHardPageFaults () { char Yaxis [32]; /*********/ /* begin */ /*********/ if (Debug) fprintf (stdout, "PresentPeakHardPageFaults()\n"); fprintf (stdout, "<div class=\"graphtitle\">Peak Paging (Hard)</div>\n\ <div class=\"graphdata\">\n"); sprintf (Yaxis, "%d", ProvideScaling(PresentPeakHardPageFaults)); GraphImageLink (true, "peak_hard_faults", "", "flt/S", Yaxis, NULL, "0"); fprintf (stdout, "Y axis is IOs per-second.\n<br>Peak %d per-second.\n", PeakHardPageFaults); if (ExtractGraph) GraphImageLink (false, "peak_hard_faults", "", "flt/S", Yaxis, NULL, "0"); fprintf (stdout, "</div>\n"); } /*****************************************************************************/ /* Read all records for the specified node in the specified time range, calculating the average and maximum for each of the data categories. Assumes it is only to be called once as it does not initialize any of the storage. */ SummarizeData () { /*********/ /* begin */ /*********/ if (Debug) fprintf (stdout, "SummarizeData()\n"); ProcessDataFiles (&SummarizeRecord, true); /* the averaging must be by the actual sample rate */ DataRecordsProcessedCount /= RecordSampleRate; if (!DataRecordsProcessedCount) return; AvePercentCPU /= DataRecordsProcessedCount; AvePercentUserModeCPU /= DataRecordsProcessedCount; AveSystemMemoryPercentInUse /= DataRecordsProcessedCount; AveNumberOfProcesses /= DataRecordsProcessedCount; AvePageSpacePercentInUse /= DataRecordsProcessedCount; AvePageFaults /= DataRecordsProcessedCount; AveHardPageFaults /= DataRecordsProcessedCount; AveBufferedIO /= DataRecordsProcessedCount; AveDirectIO /= DataRecordsProcessedCount; AveMscpIO /= DataRecordsProcessedCount; AveNetIntRxTx /= DataRecordsProcessedCount; } /*****************************************************************************/ /* Set the various accumulators according to the data in the current 'SpiRecord'. */ void SummarizeRecord () { static unsigned long RecordCount = 0, NumberOfProcesses = 0, NumberOfCom = 0, SystemMemoryPercentInUse = 0, PageSpacePercentInUse = 0, PercentCPU = 0, PercentModeCPU[HYPERSPI_CPU_MODE_COUNT] = {0, 0, 0, 0, 0, 0}, BufferedIO = 0, DirectIO = 0, MscpIO = 0, PageFaults = 0, HardPageFaults = 0; #ifndef __VAX static unsigned __int64 NetRxTx; #else static unsigned long NetRxTx; #endif int idx; unsigned long tmp; /*********/ /* begin */ /*********/ if (Debug) fprintf (stdout, "SummarizeRecord()\n"); /* usually (though not unconditionally) these remain constant! */ if (SpiRecord.NumberOfCPUs > NumberOfCPUs) NumberOfCPUs = SpiRecord.NumberOfCPUs; if (SpiRecord.SystemMemoryMBytes > SystemMemoryMBytes) SystemMemoryMBytes = SpiRecord.SystemMemoryMBytes; if (SpiRecord.PageSpaceMBytes > PageSpaceMBytes) PageSpaceMBytes = SpiRecord.PageSpaceMBytes; /* The percentage CPU values VERY OCCASIONALLY get slightly above 100%. I attribute this behaviour to slight inconsistancies between obtaining system times and actually obtaining the CPU usage data, i.e. to the delta-time used to calculate the percentage. I chose to "massage" the data at the display end rather than the collection/recording end so that this behaviour could be monitored by using the "dump" facility to examine the actual data. */ if (SpiRecord.PercentCPU > 100) SpiRecord.PercentCPU = 100; if (SpiRecord.PeakPercentCPU > 100) SpiRecord.PeakPercentCPU = 100; for (idx = 0; idx < HYPERSPI_CPU_MODE_COUNT; ++idx) if (SpiRecord.PercentModeCPU[idx] > 100) SpiRecord.PercentModeCPU[idx] = 100; /* peaks are always taken as absolutes! */ if (SpiRecord.PeakPercentCPU > PeakPercentCPU) PeakPercentCPU = SpiRecord.PeakPercentCPU; if (SpiRecord.PeakBufferedIO > PeakBufferedIO) PeakBufferedIO = SpiRecord.PeakBufferedIO; if (SpiRecord.PeakDirectIO > PeakDirectIO) PeakDirectIO = SpiRecord.PeakDirectIO; if (SpiRecord.PeakMscpIO > PeakMscpIO) PeakMscpIO = SpiRecord.PeakMscpIO; if (SpiRecord.PeakPageFaults > PeakPageFaults) PeakPageFaults = SpiRecord.PeakPageFaults; if (SpiRecord.PeakHardPageFaults > PeakHardPageFaults) PeakHardPageFaults = SpiRecord.PeakHardPageFaults; /* down convert to kB */ #ifndef __VAX *(__int64*)SpiRecord.NetIntRx = *(__int64*)SpiRecord.NetIntRx >> 10; *(__int64*)SpiRecord.NetIntTx = *(__int64*)SpiRecord.NetIntTx >> 10; #else SpiRecord.NetIntRx[0] = SpiRecord.NetIntRx[0] >> 10; SpiRecord.NetIntTx[0] = SpiRecord.NetIntTx[0] >> 10; #endif SpiRecord.PeakNetIntRx = SpiRecord.PeakNetIntRx >> 10; SpiRecord.PeakNetIntTx = SpiRecord.PeakNetIntTx >> 10; SpiRecord.PeakNetIntRxTx = SpiRecord.PeakNetIntRxTx >> 10; if (SpiRecord.PeakNetIntRxTx > PeakNetIntRxTx) PeakNetIntRxTx = SpiRecord.PeakNetIntRxTx; if (RecordSampleRate > 1) { /* X axis compression, i.e. less than one record per plot point */ PercentCPU += SpiRecord.PercentCPU; PercentUserModeCPU += SpiRecord.PercentUserModeCPU; NumberOfProcesses += SpiRecord.NumberOfProcesses; SystemMemoryPercentInUse += SpiRecord.SystemMemoryPercentInUse; PageSpacePercentInUse += SpiRecord.PageSpacePercentInUse; BufferedIO += SpiRecord.BufferedIO; DirectIO += SpiRecord.DirectIO; MscpIO += SpiRecord.MscpIO; PageFaults += SpiRecord.PageFaults; HardPageFaults += SpiRecord.HardPageFaults; #ifndef __VAX NetRxTx += *(__int64*)SpiRecord.NetIntRx + *(__int64*)SpiRecord.NetIntTx; #else NetRxTx += SpiRecord.NetIntRx[0] + SpiRecord.NetIntTx[0]; #endif if (++RecordCount < RecordSampleRate) return; /* average by dividing the accumlated values by the record sample rate */ SpiRecord.PercentCPU = PercentCPU / RecordCount; SpiRecord.PercentUserModeCPU = PercentUserModeCPU / RecordCount; SpiRecord.NumberOfProcesses = NumberOfProcesses / RecordCount; SpiRecord.SystemMemoryPercentInUse = SystemMemoryPercentInUse / RecordCount; SpiRecord.PageSpacePercentInUse = PageSpacePercentInUse / RecordCount; SpiRecord.BufferedIO = BufferedIO / RecordCount; SpiRecord.DirectIO = DirectIO / RecordCount; SpiRecord.MscpIO = MscpIO / RecordCount; SpiRecord.PageFaults = PageFaults / RecordCount; SpiRecord.HardPageFaults = HardPageFaults / RecordCount; NetRxTx /= RecordCount; /* reset the accumulators to zero */ PercentCPU = PercentUserModeCPU = NumberOfProcesses = SystemMemoryPercentInUse = PageSpacePercentInUse = BufferedIO = DirectIO = MscpIO = NetIntRxTx = PageFaults = HardPageFaults = RecordCount = 0; } else { #ifndef __VAX NetRxTx = *(__int64*)SpiRecord.NetIntRx + *(__int64*)SpiRecord.NetIntTx; #else NetRxTx = SpiRecord.NetIntRx[0] + SpiRecord.NetIntTx[0]; #endif } /* this will need to be divided by the number of records processed */ AvePercentCPU += SpiRecord.PercentCPU; PercentUserModeCPU += SpiRecord.PercentUserModeCPU; if (SpiRecord.PeakPercentUserModeCPU > PeakPercentUserModeCPU) PeakPercentUserModeCPU = SpiRecord.PeakPercentUserModeCPU; /* this will need to be divided by the number of records processed */ for (idx = 0; idx < HYPERSPI_CPU_MODE_COUNT; ++idx) AvePercentModeCPU[idx] += SpiRecord.PercentModeCPU[idx]; /* this will need to be divided by the number of records processed */ AveNumberOfProcesses += SpiRecord.NumberOfProcesses; if (SpiRecord.NumberOfProcesses > MaxNumberOfProcesses) MaxNumberOfProcesses = SpiRecord.NumberOfProcesses; /* this will need to be divided by the number of records processed */ AveNumberOfCom += SpiRecord.Computable; if (SpiRecord.Computable > MaxNumberOfCom) MaxNumberOfCom = SpiRecord.Computable; /* this will need to be divided by the number of records processed */ AveSystemMemoryPercentInUse += SpiRecord.SystemMemoryPercentInUse; if (SpiRecord.SystemMemoryPercentInUse > MaxSystemMemoryPercentInUse) MaxSystemMemoryPercentInUse = SpiRecord.SystemMemoryPercentInUse; /* this will need to be divided by the number of records processed */ AvePageSpacePercentInUse += SpiRecord.PageSpacePercentInUse; if (SpiRecord.PageSpacePercentInUse > MaxPageSpacePercentInUse) MaxPageSpacePercentInUse = SpiRecord.PageSpacePercentInUse; AveBufferedIO += (tmp = SpiRecord.BufferedIO / 60); if (tmp > MaxAveBufferedIO) MaxAveBufferedIO = tmp; AveDirectIO += (tmp = SpiRecord.DirectIO / 60); if (tmp > MaxAveDirectIO) MaxAveDirectIO = tmp; AveMscpIO += (tmp = SpiRecord.MscpIO / 60); if (tmp > MaxAveMscpIO) MaxAveMscpIO = tmp; AvePageFaults += (tmp = SpiRecord.PageFaults / 60); if (tmp > MaxAvePageFaults) MaxAvePageFaults = tmp; AveHardPageFaults += (tmp = SpiRecord.HardPageFaults / 60); if (tmp > MaxAveHardPageFaults) MaxAveHardPageFaults = tmp; AveLck += ((SpiRecord.LckLoc + SpiRecord.LckIn + SpiRecord.LckOut) / 60); AveLckLoc += (SpiRecord.LckLoc / 60); AveLckIn += (SpiRecord.LckIn / 60); AveLckOut += (SpiRecord.LckOut / 60); AveNetIntRxTx += (tmp = (unsigned long)(NetRxTx / 60)); if (tmp > MaxAveNetIntRxTx) MaxAveNetIntRxTx = tmp; } /*****************************************************************************/ /* Set the global values for the Y axis scaling factor and maximum value represented on the X axis according to the specified value (maximum out of the to-be plotted data). */ void SetScaling (int Maximum) { if (Debug) fprintf (stdout, "SetScaling() %d\n", Maximum); if (Maximum <= 10) { ScalingFactorY = 10.0; PlotScaleY = 10; } else if (Maximum <= 25) { ScalingFactorY = 4.0; PlotScaleY = 25; } else if (Maximum <= 50) { ScalingFactorY = 2.0; PlotScaleY = 50; } else if (Maximum <= 100) { ScalingFactorY = 1.0; PlotScaleY = 100; } else if (Maximum <= 250) { ScalingFactorY = 0.4; PlotScaleY = 250; } else if (Maximum <= 500) { ScalingFactorY = 0.2; PlotScaleY = 500; } else if (Maximum <= 1000) { ScalingFactorY = 0.1; PlotScaleY = 1000; } else if (Maximum <= 2500) { ScalingFactorY = 0.04; PlotScaleY = 2500; } else if (Maximum <= 5000) { ScalingFactorY = 0.02; PlotScaleY = 5000; } else if (Maximum <= 10000) { ScalingFactorY = 0.01; PlotScaleY = 10000; } else if (Maximum <= 25000) { ScalingFactorY = 0.004; PlotScaleY = 25000; } else if (Maximum <= 50000) { ScalingFactorY = 0.002; PlotScaleY = 50000; } else if (Maximum <= 100000) { ScalingFactorY = 0.001; PlotScaleY = 100000; } else { ScalingFactorY = 0.0001; PlotScaleY = 1000000; } if (Debug) fprintf (stdout, "ScalingFactorY: %f PlotScaleY: %d\n", ScalingFactorY, PlotScaleY); } /*****************************************************************************/ /* Set the global values for scaling factor for the Y axis based on the provided report/graph. If a call function is provided then ignore the (multiply set) global Provide.. booleans and use the indicated function to decide. Return the value of PlotScaleY (as a convenience only). */ int ProvideScaling (void *CallFunc) { /*********/ /* begin */ /*********/ if (Debug) fprintf (stdout, "ProvideScaling()\n"); if ((!CallFunc && ProvideBufferedIO) || (CallFunc == PresentBufferedIO)) if (IncludePeak) SetScaling (PeakBufferedIO); else SetScaling (MaxAveBufferedIO); else if ((!CallFunc && ProvidePeakBufferedIO) || (CallFunc == PresentPeakBufferedIO)) SetScaling (PeakBufferedIO); else if ((!CallFunc && ProvideDirectIO) || (CallFunc == PresentDirectIO)) if (IncludePeak) SetScaling (PeakDirectIO); else SetScaling (MaxAveDirectIO); else if ((!CallFunc && ProvidePeakDirectIO) || (CallFunc == PresentPeakDirectIO)) SetScaling (PeakDirectIO); else if ((!CallFunc && ProvideMscpIO) || (CallFunc == PresentMscpIO)) if (IncludePeak) SetScaling (PeakMscpIO); else SetScaling (MaxAveMscpIO); else if ((!CallFunc && ProvidePeakMscpIO) || (CallFunc == PresentPeakMscpIO)) SetScaling (PeakMscpIO); else if ((!CallFunc && ProvideNetInt) || (CallFunc == PresentNetInt)) if (IncludePeak) SetScaling (PeakNetIntRxTx); else SetScaling (MaxAveNetIntRxTx); else if ((!CallFunc && ProvidePeakNetInt) || (CallFunc == PresentPeakNetInt)) SetScaling (PeakNetIntRxTx); else if ((!CallFunc && ProvidePageFaults) || (CallFunc == PresentPageFaults)) if (IncludePeak) SetScaling (PeakPageFaults); else SetScaling (MaxAvePageFaults); else if ((!CallFunc && ProvidePeakPageFaults) || (CallFunc == PresentPeakPageFaults)) SetScaling (PeakPageFaults); else if ((!CallFunc && ProvideHardPageFaults) || (CallFunc == PresentHardPageFaults)) if (IncludePeak) SetScaling (PeakHardPageFaults); else SetScaling (MaxAveHardPageFaults); else if ((!CallFunc && ProvidePeakHardPageFaults) || (CallFunc == PresentPeakHardPageFaults)) SetScaling (PeakHardPageFaults); else if ((!CallFunc && ProvideProcesses) || (CallFunc == PresentProcesses)) SetScaling (MaxNumberOfProcesses); else exit (SS$_BUGCHECK); return (PlotScaleY); } /*****************************************************************************/ /* The application is being called to plot a graph of the data of the specified node in the specified time range, and generate a GIF image of that graph and return it to the browser. */ GraphData () { int Xcnt, Ycnt, AtX, AtY; /*********/ /* begin */ /*********/ if (Debug) fprintf (stdout, "GraphData()\n"); #ifdef GIF_EXPIRED CgiLibResponseHeader (200, "image/gif", "Expires: Fri, 13 Jan 1978 14:30:00 GMT\n"); #else CgiLibResponseHeader (200, "image/gif"); #endif fflush (stdout); SizeOfPlotX = NumberOfHours * 60 * XMag / RecordSampleRate; SizeOfPlotY = 100 * YMag; if (Debug) fprintf (stdout, "size %d,%d\n", SizeOfPlotX, SizeOfPlotY); HaveDataY = SizeOfMarginY / 6; PlotAllocate (SizeOfPlotX + SizeOfMarginX*2, SizeOfPlotY + SizeOfMarginY + SizeOfMarginY/2); if (ProvidePercentCPU || ProvidePercentUserModeCPU) { /* no scaling to be done for the X axis so plot it now */ PlotAxes (0); /* process specified data calling 'GraphRecordCPU()' for each record */ ProcessDataFiles (&GraphRecordCPU, true); } else if (ProvideMemory) { /* no scaling to be done for the X axis so plot it now */ PlotAxes (0); /* process specified data calling 'GraphRecordMemory()' for each record */ ProcessDataFiles (&GraphRecordMemory, true); } else if (ProvideBufferedIO || ProvidePeakBufferedIO || ProvideDirectIO || ProvidePeakDirectIO || ProvideMscpIO || ProvidePeakMscpIO || ProvideNetInt || ProvidePeakNetInt || ProvidePageFaults || ProvidePeakPageFaults || ProvideHardPageFaults || ProvidePeakHardPageFaults || ProvideProcesses) { /* plot the X and Y axes after summarizing the data and setting scaling */ SummarizeData (); ProvideScaling (NULL); PlotAxes (0); if (ProvideProcesses) { /* process data calling 'GraphRecordProcesses()' for each record */ ProcessDataFiles (&GraphRecordProcesses, true); } else { /* process data calling 'GraphRecordRange()' for each record */ ProcessDataFiles (&GraphRecordRange, true); } } else { CgiLibResponseError (FI_LI, 0, "Internal error."); exit (SS$_NORMAL); } /* if the current time is within the graphed X axis timescale */ if (CurrentMinuteFromStartOfPeriod < NumberOfHours * 60) { /* place a horizontal mark at the end of the data available line */ AtX = (CurrentMinuteFromStartOfPeriod / RecordSampleRate) * XMag; AtY = SizeOfMarginY / 4; for (Xcnt = 0; Xcnt < XMag; Xcnt++) for (Ycnt = -3; Ycnt < 3 + YMag; Ycnt++) PlotPixel (0, AtX+Xcnt, HaveDataY+Ycnt, SizeOfMarginX, 0); } /* generate and return the GIF image to the browser */ GifImage (""); } /*****************************************************************************/ /* Plot a CPU record. Either total or user-mode usage, as a line from zero to the percentage value uasage. Optionally and additionally, plot peak total or user-mode usage, or total usage (for use with the user-mode line plot) as a single point. Plot has a Y axis fixed to representPeak 100 percent. */ GraphRecordCPU () { static int RecordCount = 0; /*********/ /* begin */ /*********/ if (Debug) fprintf (stdout, "GraphRecordCPU()\n"); /* The percentage CPU values VERY OCCASIONALLY get slightly above 100%. I attribute this behaviour to slight inconsistancies between obtaining system times and actually obtaining the CPU usage data, i.e. to the delta-time used to calculate the percentage. I chose to "massage" the data at the display end rather than the collection/recording end so that this behaviour could be monitored by using the "dump" facility to examine the actual data. */ if (SpiRecord.PercentCPU > 100) SpiRecord.PercentCPU = 100; if (SpiRecord.PeakPercentCPU > 100) SpiRecord.PeakPercentCPU = 100; if (SpiRecord.PercentUserModeCPU > 100) SpiRecord.PercentUserModeCPU = 100; if (SpiRecord.PeakPercentUserModeCPU > 100) SpiRecord.PeakPercentUserModeCPU = 100; if (RecordSampleRate > 1) { /* X axis compression, i.e. less than one record per plot point */ PercentCPU += SpiRecord.PercentCPU; PercentUserModeCPU += SpiRecord.PercentUserModeCPU; /* get the maximum of the peak values read */ if (SpiRecord.PeakPercentCPU > PeakPercentCPU) PeakPercentCPU = SpiRecord.PeakPercentCPU; if (SpiRecord.PeakPercentUserModeCPU > PeakPercentUserModeCPU) PeakPercentUserModeCPU = SpiRecord.PeakPercentUserModeCPU; if (++RecordCount < RecordSampleRate) return; /* get the average of the values read */ SpiRecord.PercentCPU = PercentCPU / RecordCount; SpiRecord.PercentUserModeCPU = PercentUserModeCPU / RecordCount; /* maximum of the peak values read */ SpiRecord.PeakPercentCPU = PeakPercentCPU; SpiRecord.PeakPercentUserModeCPU = PeakPercentUserModeCPU; PercentCPU = PercentUserModeCPU = PeakPercentCPU = PeakPercentUserModeCPU = RecordCount = 0; } /*********************/ /* plot the value(s) */ /*********************/ { int Xcnt, Ycnt, ToY, AtX, AtY; AtX = (NumberOfMinutesIntoData / RecordSampleRate) * XMag; /* plot the fact we have data for this minute */ for (Xcnt = 0; Xcnt < XMag; Xcnt++) for (Ycnt = 0; Ycnt < YMag; Ycnt++) PlotPixel (0, AtX+Xcnt, HaveDataY+Ycnt, SizeOfMarginX, 0); if (ProvidePercentCPU) ToY = SpiRecord.PercentCPU * YMag; else if (ProvidePercentUserModeCPU) ToY = SpiRecord.PercentUserModeCPU * YMag; /* plot as line from zero to whatever the value is */ for (Xcnt = 0; Xcnt < XMag; Xcnt++) PlotLineY (0, 0, ToY, AtX+Xcnt, SizeOfMarginX, SizeOfMarginY); /* any extra data to plot? (is plotted in single pixels) */ if (IncludePeak) { /* CPU peak usage has been requested to be included */ if (ProvidePercentCPU) AtY = SpiRecord.PeakPercentCPU * YMag; else if (ProvidePercentUserModeCPU) AtY = SpiRecord.PeakPercentUserModeCPU * YMag; else; } else if (IncludeTotal) { /* CPU total usage has been requested to be included */ AtY = SpiRecord.PercentCPU * YMag; } else { /* no, nothing extra to plot! */ return; } /* plot the extra value as a point */ for (Xcnt = 0; Xcnt < XMag; Xcnt++) for (Ycnt = 0; Ycnt < YMag; Ycnt++) PlotPixel (PLOT_XOR, AtX+Xcnt, AtY+Ycnt, SizeOfMarginX, SizeOfMarginY); } } /*****************************************************************************/ /* Plot page space usage as a line from zero to percentage used. Plot physical memory as a point at the percentage used. Plot has a Y axis fixed to represent 100 percent. */ GraphRecordMemory () { static int RecordCount = 0; /*********/ /* begin */ /*********/ if (Debug) fprintf (stdout, "GraphRecordMemory()\n"); if (RecordSampleRate > 1) { /* X axis compression, i.e. less than one record per plot point */ SystemMemoryPercentInUse += SpiRecord.SystemMemoryPercentInUse; PageSpacePercentInUse += SpiRecord.PageSpacePercentInUse; if (++RecordCount < RecordSampleRate) return; /* get the average of the values read */ SpiRecord.SystemMemoryPercentInUse = SystemMemoryPercentInUse / RecordCount; SpiRecord.PageSpacePercentInUse = PageSpacePercentInUse / RecordCount; SystemMemoryPercentInUse = PageSpacePercentInUse = RecordCount = 0; } /*******************/ /* plot the values */ /*******************/ { int Xcnt, Ycnt, ToY, AtX, AtY; AtX = (NumberOfMinutesIntoData / RecordSampleRate) * XMag; /* plot the fact we have data for this minute */ for (Xcnt = 0; Xcnt < XMag; Xcnt++) for (Ycnt = 0; Ycnt < YMag; Ycnt++) PlotPixel (0, AtX+Xcnt, HaveDataY+Ycnt, SizeOfMarginX, 0); ToY = SpiRecord.PageSpacePercentInUse * YMag; /* plot as line from zero to whatever the value is */ for (Xcnt = 0; Xcnt < XMag; Xcnt++) PlotLineY (0, 0, ToY, AtX+Xcnt, SizeOfMarginX, SizeOfMarginY); AtY = SpiRecord.SystemMemoryPercentInUse * YMag; /* plot a point */ for (Xcnt = 0; Xcnt < XMag; Xcnt++) for (Ycnt = 0; Ycnt < YMag; Ycnt++) PlotPixel (PLOT_XOR, AtX+Xcnt, AtY+Ycnt, SizeOfMarginX, SizeOfMarginY); } } /*****************************************************************************/ /* Plot the number of processes on the system as plot-points (forming a semi-continuous line). Requires a variable range for the Y axis. That is data that can vary considerably in the maximum value represented on the Y axis. Employs a previously set variable 'ScalingFactorY' to adjust the plot values to fall within the Y axis. 'ScalingFactorY' must be set after scanning all of the specified data to determine the maximum value. */ GraphRecordProcesses () { static int RecordCount = 0; /*********/ /* begin */ /*********/ if (Debug) fprintf (stdout, "GraphRecordProcesses()\n"); if (RecordSampleRate > 1) { /* X axis compression, i.e. less than one record per plot point */ NumberOfProcesses += SpiRecord.NumberOfProcesses; /* return if still averaging according to sample rate */ if (++RecordCount < RecordSampleRate) return; /* get the average of the values read */ SpiRecord.NumberOfProcesses = NumberOfProcesses / RecordCount; /* reset the accumulator */ NumberOfProcesses = RecordCount = 0; } /*******************/ /* plot the value */ /*******************/ { int Xcnt, Ycnt, AtX, AtY; AtX = (NumberOfMinutesIntoData / RecordSampleRate) * XMag; /* plot the fact we have data for this minute */ for (Xcnt = 0; Xcnt < XMag; Xcnt++) for (Ycnt = 0; Ycnt < YMag; Ycnt++) PlotPixel (0, AtX+Xcnt, HaveDataY+Ycnt, SizeOfMarginX, 0); AtY = SpiRecord.NumberOfProcesses * ScalingFactorY * YMag; /* plot a point */ for (Xcnt = 0; Xcnt < XMag; Xcnt++) for (Ycnt = 0; Ycnt < YMag; Ycnt++) PlotPixel (PLOT_XOR, AtX+Xcnt, AtY+Ycnt, SizeOfMarginX, SizeOfMarginY); } } /*****************************************************************************/ /* Plot values that require a variable range for the Y axis. That is data that can vary considerably in the maximum value represented on the Y axis. Employs a previously set variable 'ScalingFactorY' to adjust the plot values to fall within the Y axis. 'ScalingFactorY' must be set after scanning all of the specified data to determine the maximum value. */ GraphRecordRange () { static int RecordCount = 0; static unsigned long StaticPeakValue = 0, StaticValue = 0; unsigned long PeakValue, Value; /*********/ /* begin */ /*********/ if (Debug) fprintf (stdout, "GraphRecordRange()\n"); if (ProvideBufferedIO) { Value = SpiRecord.BufferedIO; PeakValue = SpiRecord.PeakBufferedIO; } else if (ProvidePeakBufferedIO) Value = PeakValue = SpiRecord.PeakBufferedIO; else if (ProvideDirectIO) { Value = SpiRecord.DirectIO; PeakValue = SpiRecord.PeakDirectIO; } else if (ProvidePeakDirectIO) Value = PeakValue = SpiRecord.PeakDirectIO; else if (ProvideMscpIO) { Value = SpiRecord.MscpIO; PeakValue = SpiRecord.PeakMscpIO; } else if (ProvidePeakMscpIO) Value = PeakValue = SpiRecord.PeakMscpIO; else if (ProvideNetInt) { #ifndef __VAX Value = (unsigned long)(((*(__int64*)SpiRecord.NetIntRx + *(__int64*)SpiRecord.NetIntTx)) >> 10); #else Value = (SpiRecord.NetIntRx[0] + SpiRecord.NetIntTx[0]) >> 10; #endif PeakValue = SpiRecord.PeakNetIntRxTx; } else if (ProvidePeakNetInt) Value = PeakValue = SpiRecord.PeakNetIntRxTx; else if (ProvidePageFaults) { Value = SpiRecord.PageFaults; PeakValue = SpiRecord.PeakPageFaults; } else if (ProvidePeakPageFaults) Value = PeakValue = SpiRecord.PeakPageFaults; else if (ProvideHardPageFaults) { Value = SpiRecord.HardPageFaults; PeakValue = SpiRecord.PeakHardPageFaults; } else if (ProvidePeakHardPageFaults) Value = PeakValue = SpiRecord.PeakHardPageFaults; if (RecordSampleRate > 1) { /* X axis compression, i.e. less than one record per plot point */ StaticValue += Value; /* get the maximum of the peak values read */ if (PeakValue > StaticPeakValue) StaticPeakValue = PeakValue; if (++RecordCount < RecordSampleRate) return; /* get the average of the values read */ Value = StaticValue / RecordCount; /* maximum of the peak values read */ PeakValue = StaticPeakValue; StaticValue = StaticPeakValue = RecordCount = 0; } if (Debug) fprintf (stdout, "Value: %d PeakValue: %d\n", Value, PeakValue); /*********************/ /* plot the value(s) */ /*********************/ { int Xcnt, Ycnt, ToY, AtX, AtY; AtX = (NumberOfMinutesIntoData / RecordSampleRate) * XMag; /* plot the fact we have data for this minute */ for (Xcnt = 0; Xcnt < XMag; Xcnt++) for (Ycnt = 0; Ycnt < YMag; Ycnt++) PlotPixel (0, AtX+Xcnt, HaveDataY+Ycnt, SizeOfMarginX, 0); if (ProvideBufferedIO || ProvideDirectIO || ProvideMscpIO || ProvideNetInt || ProvidePageFaults || ProvideHardPageFaults) ToY = (int)((float)(Value / 60) * ScalingFactorY) * YMag; else ToY = (int)((float)PeakValue * ScalingFactorY) * YMag; /* plot as line from zero to whatever the value is */ for (Xcnt = 0; Xcnt < XMag; Xcnt++) PlotLineY (0, 0, ToY, AtX+Xcnt, SizeOfMarginX, SizeOfMarginY); if (IncludePeak) AtY = (int)((float)PeakValue * ScalingFactorY) * YMag; else { /* no, nothing extra to plot! */ return; } /* plot peak value as a point */ for (Xcnt = 0; Xcnt < XMag; Xcnt++) for (Ycnt = 0; Ycnt < YMag; Ycnt++) PlotPixel (PLOT_XOR, AtX+Xcnt, AtY+Ycnt, SizeOfMarginX, SizeOfMarginY); } } /*****************************************************************************/ /* */ ListProcessedData () { /*********/ /* begin */ /*********/ if (Debug) fprintf (stdout, "ListProcessedData()\n"); CgiLibResponseHeader (200, "text/html"); fprintf (stdout, "<!DOCTYPE html>\n\ <html>\n\ <head>\n\ <meta http-equiv=\"X-UA-Compatible\" content=\"IE=edge\">\n\ <meta name=\"generator\" content=\"%s\">\n\ <meta name=\"copyright\" content=\"%s - GPL licensed\">\n\ <meta name=\"environment\" content=\"%s\">\n\ <title>HyperSPI - Processed Data - %s</title>\n\ <style type=\"text/css\">\n\ %s\ </style>\n\ %s\ </head>\n\ <body>\n", SoftwareID, SoftwareCopy, CgiEnvironmentPtr, CgiFormNodePtr, DefaultStyle, StyleSheetPtr); fprintf (stdout, "<div class=\"header\">\ <div class=\"hyperSPI\">HyperSPI</div>\ <div class=\"heading\">Processed Data for %s</div>\ <div class=\"datetime\">%d %s %d %0.2d:%0.2d</div>\ </div>\n", CgiFormNodePtr, CurrentNumTime[2], MonthName[CurrentNumTime[1]], CurrentNumTime[0], CurrentNumTime[3], CurrentNumTime[4]); fprintf (stdout, "<div class=\"content\">\n"); HttpHasBeenOutput = true; ProcessDataFiles (&ListProcessedRecord, true); if (DataFilesProcessedCount) fputs ("</pre>\n</div>\n", stdout); else fputs ("<p>No matching data files.\n</div>\n", stdout); ButtonBar (2); fprintf (stdout, "</body>\n</html>\n"); } /*****************************************************************************/ /* */ void ListProcessedRecord () { static int PrevDay = -1, PrevHour = -1; /*********/ /* begin */ /*********/ if (Debug) fprintf (stdout, "ListProcessedRecord()\n"); if (DataDay != PrevDay) { if (PrevDay != -1) fputs ("</pre>\n", stdout); PrevDay = DataDay; fprintf (stdout, "<div class=\"fileday\">%s %02.02d %s %d</div>\n\ <div class=\"filename\">%s</div>\n\ <pre>", CgiFormNodePtr, DataDay, MonthName[DataMonth], DataYear, DataFileName); } if (SpiRecord.Hour != PrevHour) { if (PrevHour != -1) fputs ("\n", stdout); PrevHour = SpiRecord.Hour; fprintf (stdout, "hh:mm CPU usr mem pge b-IO peak d-IO peak mscp peak flts peak \ hard peak \ NI rx peak NI tx peak rx+tx peak\n\ ----- --- --- --- --- ---- ---- ---- ---- ---- ---- ----- ----- \ ---- ---- \ ------------ ------------ ------------ ------------ ------------\n"); } fprintf (stdout, "%02.02d:%02.02d %3d %3d %3d %3d \ %4d %4d %4d %4d %4d %4d %5d %5d %4d %4d ", SpiRecord.Hour, SpiRecord.Minute, SpiRecord.PercentCPU, SpiRecord.PercentUserModeCPU, SpiRecord.SystemMemoryPercentInUse, SpiRecord.PageSpacePercentInUse, SpiRecord.BufferedIO / 60, SpiRecord.PeakBufferedIO, SpiRecord.DirectIO / 60, SpiRecord.PeakDirectIO, SpiRecord.MscpIO / 60, SpiRecord.PeakMscpIO, SpiRecord.PageFaults / 60, SpiRecord.PeakPageFaults, SpiRecord.HardPageFaults / 60, SpiRecord.PeakHardPageFaults); #ifndef __VAX fprintf (stdout, "%12Lu %12u %12Lu %12u %12u\n", *(__int64*)&SpiRecord.NetIntRx, SpiRecord.PeakNetIntRx, *(__int64*)&SpiRecord.NetIntTx, SpiRecord.PeakNetIntTx, SpiRecord.PeakNetIntRxTx); #else fprintf (stdout, "%12u %12u %12u %12u %12u\n", SpiRecord.NetIntRx[0], SpiRecord.PeakNetIntRx, SpiRecord.NetIntTx[0], SpiRecord.PeakNetIntTx, SpiRecord.PeakNetIntRxTx); #endif } /*****************************************************************************/ /* */ void DumpData () { /*********/ /* begin */ /*********/ if (Debug) fprintf (stdout, "DumpData()\n"); CgiLibResponseHeader (200, "text/html"); fprintf (stdout, "<!DOCTYPE html>\n\ <html>\n\ <head>\n\ <meta http-equiv=\"X-UA-Compatible\" content=\"IE=edge\">\n\ <meta name=\"generator\" content=\"%s\">\n\ <meta name=\"copyright\" content=\"%s - GPL licensed\">\n\ <meta name=\"environment\" content=\"%s\">\n\ <title>HyperSPI - Data Dump - %s</title>\n\ <style type=\"text/css\">\n\ %s\ </style>\n\ %s\ </head>\n\ <body>\n", SoftwareID, SoftwareCopy, CgiEnvironmentPtr, CgiFormNodePtr, DefaultStyle, StyleSheetPtr); fprintf (stdout, "<div class=\"header\">\ <div class=\"hyperSPI\">HyperSPI</div>\ <div class=\"heading\">Data Dump for %s</div>\ <div class=\"datetime\">%d %s %d %0.2d:%0.2d</div>\ </div>\n", CgiFormNodePtr, CurrentNumTime[2], MonthName[CurrentNumTime[1]], CurrentNumTime[0], CurrentNumTime[3], CurrentNumTime[4]); fprintf (stdout, "<div class=\"content\">\n"); HttpHasBeenOutput = true; ProcessDataFiles (&DumpRecord, true); if (DataFilesProcessedCount) fputs ("</pre>\n</div>\n", stdout); else fputs ("<p>No matching data files.\n</div>\n", stdout); ButtonBar (2); fprintf (stdout, "</body>\n</html>\n"); } /*****************************************************************************/ /* */ void DumpRecord() { static int PrevDay = -1, PrevHour = -1; /*********/ /* begin */ /*********/ if (Debug) fprintf (stdout, "DumpRecord()\n"); if (DataDay != PrevDay) { if (PrevDay != -1) fputs ("</pre>\n", stdout); PrevDay = DataDay; fprintf (stdout, "<div class=\"fileday\">%s %02.02d %s %d</div>\n\ <div class=\"filename\">%s</div>\n\ <pre>", CgiFormNodePtr, DataDay, MonthName[DataMonth], DataYear, DataFileName); } if (SpiRecord.Hour != PrevHour) { fputs ("\n", stdout); PrevHour = SpiRecord.Hour; fprintf (stdout, "hh:mm prc CPU pk usr pk mem pge buf-IO peak \ dir-IO peak mscp-IO peak faults peak hard peak Com \ int mps krn exe sup usr Lck-Loc Lck-In Lck-Out \ rx peak tx peak rx+tx peak\n\ ----- --- --- --- --- --- --- --- ------- ------- \ ------- ------- ------- ------- ------- ------- ----- ----- --- \ --- --- --- --- --- --- ------- ------- ------- \ ------------ ------------ ------------ ------------ ------------\n"); } fprintf (stdout, "%02.02d:%02.02d %3d %3d %3d %3d %3d %3d %3d \ %7d %7d %7d %7d %7d %7d %7d %7d %5d %5d %3d \ %3d %3d %3d %3d %3d %3d %7d %7d %7d ", SpiRecord.Hour, SpiRecord.Minute, SpiRecord.NumberOfProcesses, SpiRecord.PercentCPU, SpiRecord.PeakPercentCPU, SpiRecord.PercentUserModeCPU, SpiRecord.PeakPercentUserModeCPU, SpiRecord.SystemMemoryPercentInUse, SpiRecord.PageSpacePercentInUse, SpiRecord.BufferedIO, SpiRecord.PeakBufferedIO, SpiRecord.DirectIO, SpiRecord.PeakDirectIO, SpiRecord.MscpIO, SpiRecord.PeakMscpIO, SpiRecord.PageFaults, SpiRecord.PeakPageFaults, SpiRecord.HardPageFaults, SpiRecord.PeakHardPageFaults, SpiRecord.Computable, SpiRecord.PercentModeCPU[HYPERSPI_MODE_INTERRUPT], SpiRecord.PercentModeCPU[HYPERSPI_MODE_MULTIPROC], SpiRecord.PercentModeCPU[HYPERSPI_MODE_KERNEL], SpiRecord.PercentModeCPU[HYPERSPI_MODE_EXECUTIVE], SpiRecord.PercentModeCPU[HYPERSPI_MODE_SUPERVISOR], SpiRecord.PercentModeCPU[HYPERSPI_MODE_USER], SpiRecord.LckLoc, SpiRecord.LckIn, SpiRecord.LckOut); #ifndef __VAX fprintf (stdout, "%12Lu %12u %12Lu %12u %12u\n", *(__int64*)&SpiRecord.NetIntRx, SpiRecord.PeakNetIntRx, *(__int64*)&SpiRecord.NetIntTx, SpiRecord.PeakNetIntTx, SpiRecord.PeakNetIntRxTx); #else fprintf (stdout, "%12u %12u %12u %12u %12u\n", SpiRecord.NetIntRx[0], SpiRecord.PeakNetIntRx, SpiRecord.NetIntTx[0], SpiRecord.PeakNetIntTx, SpiRecord.PeakNetIntRxTx); #endif } /*****************************************************************************/ /* The first parameter to this function is the address (a pointer to) the function used to process each file name found. */ int ProcessDataFiles ( int (*ProcessFileFunction)(), boolean OpenDataFile ) { char *cptr, *sptr; int status; char ExpandedFileName [256], Scratch [256]; struct FAB SearchFab; struct RAB SearchRab; struct NAM SearchNam; /*********/ /* begin */ /*********/ if (Debug) fprintf (stdout, "ProcessDataFiles() |%s|\n", DataFileSpec); DataFilesFoundCount = DataFilesProcessedCount = DataRecordsReadCount = DataRecordsProcessedCount = 0; SearchFab = cc$rms_fab; SearchFab.fab$l_fna = DataFileSpec; SearchFab.fab$b_fns = DataFileSpecLength; SearchFab.fab$l_fop = FAB$M_NAM; SearchFab.fab$l_nam = &SearchNam; SearchNam = cc$rms_nam; SearchNam.nam$l_esa = ExpandedFileName; SearchNam.nam$b_ess = sizeof(ExpandedFileName)-1; SearchNam.nam$l_rsa = DataFileName; SearchNam.nam$b_rss = sizeof(DataFileName)-1; if (VMSnok (status = sys$parse (&SearchFab, 0, 0))) { if (Debug) fprintf (stdout, "sys$parse() %%X%08.08X\n", status); CgiLibResponseError (FI_LI, status, DataFileSpec); exit (SS$_NORMAL); } while (VMSok (status = sys$search (&SearchFab, 0, 0))) { DataFilesFoundCount++; SearchNam.nam$l_ver[SearchNam.nam$b_ver] = '\0'; DataFileNameLength = SearchNam.nam$b_rsl; if (Debug) fprintf (stdout, "DataFileName |%s|\n", DataFileName); /* Pull the node name and time components from the data file name. Format: "HYPERSPI_v_node_ddmmyy.DAT" ("v" represents the data file version, a single digit number) */ cptr = SearchNam.nam$l_name; /* skip "HYPERSPI_version_" */ while (*cptr && *cptr != '_') cptr++; if (*cptr) cptr++; while (*cptr && *cptr != '_') cptr++; if (*cptr) cptr++; /* get the node name this data represents */ sptr = DataNode; while (*cptr && *cptr != '_') *sptr++ = *cptr++; *sptr = '\0'; if (*cptr) cptr++; /* get the day, month and year */ sptr = Scratch; if (*cptr) *sptr++ = *cptr++; if (*cptr) *sptr++ = *cptr++; *sptr = '\0'; DataDay = atoi (Scratch); sptr = Scratch; if (*cptr) *sptr++ = *cptr++; if (*cptr) *sptr++ = *cptr++; *sptr = '\0'; DataMonth = atoi (Scratch); sptr = Scratch; if (*cptr) *sptr++ = *cptr++; if (*cptr) *sptr++ = *cptr++; *sptr = '\0'; DataYear = atoi (Scratch); if (Debug) fprintf (stdout, "data-file-name |%s|%d|%d|%d|\n", DataNode, DataDay, DataMonth, DataYear); /* filter on year, month and day */ if (DataYear <= 90) DataYear += 2000; else DataYear += 1900; if (DataYear < FromYear || DataYear > ToYear) continue; if (DataYear == FromYear) { if (DataMonth < FromMonth) continue; if (DataMonth == FromMonth && DataDay < FromDay) continue; } if (DataYear == ToYear) { if (DataMonth > ToMonth) continue; if (DataMonth == ToMonth && DataDay > ToDay) continue; } DataFilesProcessedCount++; /* can be used to just count the number of matching files! */ if (ProcessFileFunction == NULL) continue; if (OpenDataFile) ProcessDataFileRecords (ProcessFileFunction); else /* by pointer, call the function to process this file name */ (*ProcessFileFunction) (); } if (Debug) fprintf (stdout, "sys$search() %%X%08.08X\n", status); if (SearchFab.fab$l_sts == RMS$_FNF || SearchFab.fab$l_sts == RMS$_NMF) return (SS$_NORMAL); CgiLibResponseError (FI_LI, status, DataFileSpec); exit (SS$_NORMAL); } /*****************************************************************************/ /* Open the 'DataFileName', read each record and call a specified function to process it, then close the file. The first parameter to this function is the address (a pointer to) the function used to process the SPI data record. */ ProcessDataFileRecords (int (*ProcessDataFunction)(struct HyperSpiData*)) { static long LibJulianDate = LIB$K_JULIAN_DATE; static unsigned short PrevNumTime [7] = {0,0,0,0,0,0,0}; int status; unsigned long JulDate, BinTime [2]; struct FAB DataFileFab; struct RAB DataFileRab; /*********/ /* begin */ /*********/ if (Debug) fprintf (stdout, "ProcessDataFileRecords() |%s|\n", DataFileName); DataFileFab = cc$rms_fab; DataFileFab.fab$b_fac = FAB$M_GET; DataFileFab.fab$l_fna = DataFileName; DataFileFab.fab$b_fns = DataFileNameLength; DataFileFab.fab$b_shr = FAB$M_SHRGET | FAB$M_SHRPUT; if (VMSnok (status = sys$open (&DataFileFab, 0, 0))) { if (Debug) fprintf (stdout, "sys$open() %%X%08.08X\n", status); if (status == RMS$_FNF) { CgiLibResponseError (FI_LI, status, DataNode); exit (SS$_NORMAL); } CgiLibResponseError (FI_LI, status, DataNode); exit (SS$_NORMAL); } DataFileRab = cc$rms_rab; DataFileRab.rab$l_fab = &DataFileFab; /* 2 buffers and read ahead performance option */ DataFileRab.rab$b_mbf = 2; DataFileRab.rab$l_rop = RAB$M_RAH; DataFileRab.rab$l_ubf = (char*)&SpiRecord; DataFileRab.rab$w_usz = sizeof(SpiRecord); if (VMSnok (status = sys$connect (&DataFileRab, 0, 0))) { if (Debug) fprintf (stdout, "sys$connect() %%X%08.08X\n", status); sys$close (&DataFileFab, 0, 0); CgiLibResponseError (FI_LI, status, DataNode); exit (SS$_NORMAL); } while (VMSok (status = sys$get (&DataFileRab, 0, 0))) { DataRecordsReadCount++; /* filter on hour and minute */ if (SpiRecord.Day == FromDay) { if (SpiRecord.Hour < FromHour) continue; if (SpiRecord.Hour == FromHour && SpiRecord.Minute < FromMinute) continue; } if (SpiRecord.Day == ToDay) { if (SpiRecord.Hour > ToHour) continue; if (SpiRecord.Hour == ToHour && SpiRecord.Minute > ToMinute) continue; } if (SpiRecord.Hour != PrevNumTime[3] || SpiRecord.Day != PrevNumTime[2] || SpiRecord.Month != PrevNumTime[1] || SpiRecord.Year != PrevNumTime[0]) { PrevNumTime[3] = SpiRecord.Hour; PrevNumTime[2] = SpiRecord.Day; PrevNumTime[1] = SpiRecord.Month; PrevNumTime[0] = SpiRecord.Year; lib$cvt_vectim (&PrevNumTime, &BinTime); lib$cvt_from_internal_time (&LibJulianDate, &JulDate, &BinTime); NumberOfDaysIntoData = JulDate - FromJulianDate; StartMinuteOfData = NumberOfDaysIntoData * 1440; if (Debug) fprintf (stdout, "NumberOfDaysIntoData: %d StartMinuteOfData: %d\n", NumberOfDaysIntoData, StartMinuteOfData); } NumberOfMinutesIntoData = StartMinuteOfData + ((SpiRecord.Hour * 60) + SpiRecord.Minute) - StartMinuteOnFirstDay; DataRecordsProcessedCount++; /* by pointer, call the function to process this SPI data record */ (*ProcessDataFunction) (&SpiRecord); } sys$close (&DataFileFab, 0, 0); if (status == RMS$_EOF) status = SS$_NORMAL; if (VMSnok (status)) { CgiLibResponseError (FI_LI, status, DataNode); exit (SS$_NORMAL); } } /*****************************************************************************/ /* The specified parameter is the name of the file, excluding directory and file type, just the name. Construct a full HTML file name and open it, read each record just passing the contents into the HTTP output stream, the close the file. If the file cannot be found the status RMS$_FNF is returned, all other errors are reported and the image exits. */ IncludeFile (char *Name) { int status; char FileName [256], Line [256], Scratch [256]; struct FAB FileFab; struct RAB FileRab; /*********/ /* begin */ /*********/ if (Debug) fprintf (stdout, "IncludeFile() |%s|\n", Name); sprintf (FileName, "%s%s.HTML", HyperSpiDirectoryPtr, Name); if (Debug) fprintf (stdout, "FileName |%s|\n", FileName); FileFab = cc$rms_fab; FileFab.fab$b_fac = FAB$M_GET; FileFab.fab$l_fna = FileName; FileFab.fab$b_fns = strlen(FileName); FileFab.fab$b_shr = FAB$M_SHRGET; if (VMSnok (status = sys$open (&FileFab, 0, 0))) { if (Debug) fprintf (stdout, "sys$open() %%X%08.08X\n", status); if (status == RMS$_FNF || status == RMS$_DNF) return (status); CgiLibResponseError (FI_LI, status, Name); exit (SS$_NORMAL); } FileRab = cc$rms_rab; FileRab.rab$l_fab = &FileFab; /* 2 buffers and read ahead performance option */ FileRab.rab$b_mbf = 2; FileRab.rab$l_rop = RAB$M_RAH; FileRab.rab$l_ubf = Line; FileRab.rab$w_usz = sizeof(Line)-1; if (VMSnok (status = sys$connect (&FileRab, 0, 0))) { if (Debug) fprintf (stdout, "sys$connect() %%X%08.08X\n", status); sys$close (&FileFab, 0, 0); CgiLibResponseError (FI_LI, status, Name); exit (SS$_NORMAL); } while (VMSok (status = sys$get (&FileRab, 0, 0))) { Line[FileRab.rab$w_rsz++] = '\r'; Line[FileRab.rab$w_rsz++] = '\n'; Line[FileRab.rab$w_rsz] = '\0'; if (Debug) fprintf (stdout, "Line |%s|\n", Line); fputs (Line, stdout); } sys$close (&FileFab, 0, 0); if (status == RMS$_EOF) return (status); CgiLibResponseError (FI_LI, status, Name); exit (SS$_NORMAL); } /*****************************************************************************/ /* Return a pointer to a unique number built up of time components. Used to place a unique component into the URL of a graph GIF, ensuring a cached version is not retrieved. */ char* UniqueNumber () { static char String [16]; unsigned long BinTime [2]; unsigned short NumTime [7]; sys$gettim (&BinTime); sys$numtim (&NumTime, &BinTime); sprintf (String, "%d%d%d%d%d%d%d", NumTime[0] % 10, NumTime[1], NumTime[2], NumTime[3], NumTime[4], NumTime[5], NumTime[6]); return (String); } /*****************************************************************************/ /* Translate the logical name HYPERSPI_AGENT_PID containing the agent process ID (binary) and issue a $FORCEX against the image. */ int ShutdownAgent () { static $DESCRIPTOR (LnmSystemDsc, "LNM$SYSTEM"); static $DESCRIPTOR (PidLogNameDsc, "HYPERSPI_AGENT_PID"); static unsigned long Pid; static struct { unsigned short buf_len; unsigned short item; void *buf_addr; unsigned short *short_ret_len; } LnmPidItem [] = { { sizeof(Pid), LNM$_STRING, &Pid, 0 }, { 0,0,0,0 } }; int status; /*********/ /* begin */ /*********/ status = sys$trnlnm (0, &LnmSystemDsc, &PidLogNameDsc, 0, &LnmPidItem); if (VMSok (status)) status = sys$forcex (&Pid, 0, SS$_NORMAL); return (status); } /****************************************************************************/ /* */ char* SystemNodeName () { static char SyiNodeName [15+1]; static unsigned short SyiNodeNameLength; static struct { short BufferLength; short ItemCode; void *BufferPtr; void *LengthPtr; } SyiItems [] = { { 15, SYI$_NODENAME, &SyiNodeName, &SyiNodeNameLength }, { 0,0,0,0 } }; struct { unsigned long Status; unsigned long Reserved; } IOsb; int status; /*********/ /* begin */ /*********/ status = sys$getsyiw (0, 0, 0, &SyiItems, &IOsb, 0, 0); if (Debug) fprintf (stdout, "sys$getsyiw() %%X%08.08X IOsb: %%X%08.08X\n", status, IOsb.Status); if (VMSok (IOsb.Status)) SyiNodeName[SyiNodeNameLength] = '\0'; else sprintf(SyiNodeName, "%%X%08.08X", IOsb.Status); if (Debug) fprintf (stdout, "SyiNodeName |%s|\n", SyiNodeName); return (SyiNodeName); } /****************************************************************************/ /* Does a case-insensitive, character-by-character string compare and returns true if two strings are the same, or false if not. If a maximum number of characters are specified only those will be compared, if the entire strings should be compared then specify the number of characters as 0. */ boolean strsame ( char *sptr1, char *sptr2, int count ) { while (*sptr1 && *sptr2) { if (toupper (*sptr1++) != toupper (*sptr2++)) return (false); if (count) if (!--count) return (true); } if (*sptr1 || *sptr2) return (false); else return (true); } /*****************************************************************************/