8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 * or http://www.opensolaris.org/os/licensing.
10 * See the License for the specific language governing permissions
11 * and limitations under the License.
12 *
13 * When distributing Covered Code, include this CDDL HEADER in each
14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 * If applicable, add the following below this CDDL HEADER, with the
16 * fields enclosed by brackets "[]" replaced with your own identifying
17 * information: Portions Copyright [yyyy] [name of copyright owner]
18 *
19 * CDDL HEADER END
20 */
21
22 /*
23 * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
24 * Use is subject to license terms.
25 */
26
27 /*
28 * Copyright (c) 2011, Joyent, Inc. All rights reserved.
29 * Copyright (c) 2012 by Delphix. All rights reserved.
30 */
31
32 #include <stdlib.h>
33 #include <strings.h>
34 #include <errno.h>
35 #include <unistd.h>
36 #include <dt_impl.h>
37 #include <assert.h>
38 #include <alloca.h>
39 #include <limits.h>
40
41 #define DTRACE_AHASHSIZE 32779 /* big 'ol prime */
42
43 /*
44 * Because qsort(3C) does not allow an argument to be passed to a comparison
45 * function, the variables that affect comparison must regrettably be global;
46 * they are protected by a global static lock, dt_qsort_lock.
47 */
48 static pthread_mutex_t dt_qsort_lock = PTHREAD_MUTEX_INITIALIZER;
1274 {
1275 dt_ahashent_t *h, *next;
1276 dt_ahash_t *hash = &dtp->dt_aggregate.dtat_hash;
1277
1278 for (h = hash->dtah_all; h != NULL; h = next) {
1279 /*
1280 * dt_aggwalk_rval() can potentially remove the current hash
1281 * entry; we need to load the next hash entry before calling
1282 * into it.
1283 */
1284 next = h->dtahe_nextall;
1285
1286 if (dt_aggwalk_rval(dtp, h, func(&h->dtahe_data, arg)) == -1)
1287 return (-1);
1288 }
1289
1290 return (0);
1291 }
1292
1293 static int
1294 dt_aggregate_walk_sorted(dtrace_hdl_t *dtp,
1295 dtrace_aggregate_f *func, void *arg,
1296 int (*sfunc)(const void *, const void *))
1297 {
1298 dt_aggregate_t *agp = &dtp->dt_aggregate;
1299 dt_ahashent_t *h, **sorted;
1300 dt_ahash_t *hash = &agp->dtat_hash;
1301 size_t i, nentries = 0;
1302
1303 for (h = hash->dtah_all; h != NULL; h = h->dtahe_nextall)
1304 nentries++;
1305
1306 sorted = dt_alloc(dtp, nentries * sizeof (dt_ahashent_t *));
1307
1308 if (sorted == NULL)
1309 return (-1);
1310
1311 for (h = hash->dtah_all, i = 0; h != NULL; h = h->dtahe_nextall)
1312 sorted[i++] = h;
1313
1314 (void) pthread_mutex_lock(&dt_qsort_lock);
1315
1316 if (sfunc == NULL) {
1317 dt_aggregate_qsort(dtp, sorted, nentries,
1318 sizeof (dt_ahashent_t *), NULL);
1319 } else {
1320 /*
1321 * If we've been explicitly passed a sorting function,
1322 * we'll use that -- ignoring the values of the "aggsortrev",
1323 * "aggsortkey" and "aggsortkeypos" options.
1324 */
1325 qsort(sorted, nentries, sizeof (dt_ahashent_t *), sfunc);
1326 }
1327
1328 (void) pthread_mutex_unlock(&dt_qsort_lock);
1329
1330 for (i = 0; i < nentries; i++) {
1331 h = sorted[i];
1332
1333 if (dt_aggwalk_rval(dtp, h, func(&h->dtahe_data, arg)) == -1) {
1334 dt_free(dtp, sorted);
1335 return (-1);
1336 }
1337 }
1338
1339 dt_free(dtp, sorted);
1340 return (0);
1341 }
1342
1343 int
1344 dtrace_aggregate_walk_sorted(dtrace_hdl_t *dtp,
1345 dtrace_aggregate_f *func, void *arg)
1346 {
1347 return (dt_aggregate_walk_sorted(dtp, func, arg, NULL));
1348 }
1349
1350 int
1351 dtrace_aggregate_walk_keysorted(dtrace_hdl_t *dtp,
1352 dtrace_aggregate_f *func, void *arg)
1353 {
1354 return (dt_aggregate_walk_sorted(dtp, func,
1355 arg, dt_aggregate_varkeycmp));
1356 }
1357
1358 int
1359 dtrace_aggregate_walk_valsorted(dtrace_hdl_t *dtp,
1360 dtrace_aggregate_f *func, void *arg)
1843
1844 if (zaggdata != NULL) {
1845 for (i = 0; i < naggvars; i++)
1846 dt_free(dtp, zaggdata[i].dtahe_data.dtada_data);
1847 }
1848
1849 dt_free(dtp, zaggdata);
1850 dt_free(dtp, sorted);
1851 dt_free(dtp, remap);
1852 dt_free(dtp, map);
1853
1854 return (rval);
1855 }
1856
1857 int
1858 dtrace_aggregate_print(dtrace_hdl_t *dtp, FILE *fp,
1859 dtrace_aggregate_walk_f *func)
1860 {
1861 dt_print_aggdata_t pd;
1862
1863 pd.dtpa_dtp = dtp;
1864 pd.dtpa_fp = fp;
1865 pd.dtpa_allunprint = 1;
1866
1867 if (func == NULL)
1868 func = dtrace_aggregate_walk_sorted;
1869
1870 if ((*func)(dtp, dt_print_agg, &pd) == -1)
1871 return (dt_set_errno(dtp, dtp->dt_errno));
1872
1873 return (0);
1874 }
1875
1876 void
1877 dtrace_aggregate_clear(dtrace_hdl_t *dtp)
1878 {
1879 dt_aggregate_t *agp = &dtp->dt_aggregate;
1880 dt_ahash_t *hash = &agp->dtat_hash;
1881 dt_ahashent_t *h;
1882 dtrace_aggdata_t *data;
|
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 * or http://www.opensolaris.org/os/licensing.
10 * See the License for the specific language governing permissions
11 * and limitations under the License.
12 *
13 * When distributing Covered Code, include this CDDL HEADER in each
14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 * If applicable, add the following below this CDDL HEADER, with the
16 * fields enclosed by brackets "[]" replaced with your own identifying
17 * information: Portions Copyright [yyyy] [name of copyright owner]
18 *
19 * CDDL HEADER END
20 */
21
22 /*
23 * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
24 * Use is subject to license terms.
25 */
26
27 /*
28 * Copyright (c) 2013, Joyent, Inc. All rights reserved.
29 * Copyright (c) 2012 by Delphix. All rights reserved.
30 */
31
32 #include <stdlib.h>
33 #include <strings.h>
34 #include <errno.h>
35 #include <unistd.h>
36 #include <dt_impl.h>
37 #include <assert.h>
38 #include <alloca.h>
39 #include <limits.h>
40
41 #define DTRACE_AHASHSIZE 32779 /* big 'ol prime */
42
43 /*
44 * Because qsort(3C) does not allow an argument to be passed to a comparison
45 * function, the variables that affect comparison must regrettably be global;
46 * they are protected by a global static lock, dt_qsort_lock.
47 */
48 static pthread_mutex_t dt_qsort_lock = PTHREAD_MUTEX_INITIALIZER;
1274 {
1275 dt_ahashent_t *h, *next;
1276 dt_ahash_t *hash = &dtp->dt_aggregate.dtat_hash;
1277
1278 for (h = hash->dtah_all; h != NULL; h = next) {
1279 /*
1280 * dt_aggwalk_rval() can potentially remove the current hash
1281 * entry; we need to load the next hash entry before calling
1282 * into it.
1283 */
1284 next = h->dtahe_nextall;
1285
1286 if (dt_aggwalk_rval(dtp, h, func(&h->dtahe_data, arg)) == -1)
1287 return (-1);
1288 }
1289
1290 return (0);
1291 }
1292
1293 static int
1294 dt_aggregate_total(dtrace_hdl_t *dtp, boolean_t clear)
1295 {
1296 dt_ahashent_t *h;
1297 dtrace_aggdata_t **total;
1298 dtrace_aggid_t max = DTRACE_AGGVARIDNONE, id;
1299 dt_aggregate_t *agp = &dtp->dt_aggregate;
1300 dt_ahash_t *hash = &agp->dtat_hash;
1301 uint32_t tflags;
1302
1303 tflags = DTRACE_A_TOTAL | DTRACE_A_HASNEGATIVES | DTRACE_A_HASPOSITIVES;
1304
1305 /*
1306 * If we need to deliver per-aggregation totals, we're going to take
1307 * three passes over the aggregate: one to clear everything out and
1308 * determine our maximum aggregation ID, one to actually total
1309 * everything up, and a final pass to assign the totals to the
1310 * individual elements.
1311 */
1312 for (h = hash->dtah_all; h != NULL; h = h->dtahe_nextall) {
1313 dtrace_aggdata_t *aggdata = &h->dtahe_data;
1314
1315 if ((id = dt_aggregate_aggvarid(h)) > max)
1316 max = id;
1317
1318 aggdata->dtada_total = 0;
1319 aggdata->dtada_flags &= ~tflags;
1320 }
1321
1322 if (clear || max == DTRACE_AGGVARIDNONE)
1323 return (0);
1324
1325 total = dt_zalloc(dtp, (max + 1) * sizeof (dtrace_aggdata_t *));
1326
1327 if (total == NULL)
1328 return (-1);
1329
1330 for (h = hash->dtah_all; h != NULL; h = h->dtahe_nextall) {
1331 dtrace_aggdata_t *aggdata = &h->dtahe_data;
1332 dtrace_aggdesc_t *agg = aggdata->dtada_desc;
1333 dtrace_recdesc_t *rec;
1334 caddr_t data;
1335 int64_t val, *addr;
1336
1337 rec = &agg->dtagd_rec[agg->dtagd_nrecs - 1];
1338 data = aggdata->dtada_data;
1339 addr = (int64_t *)(uintptr_t)(data + rec->dtrd_offset);
1340
1341 switch (rec->dtrd_action) {
1342 case DTRACEAGG_STDDEV:
1343 val = dt_stddev((uint64_t *)addr, 1);
1344 break;
1345
1346 case DTRACEAGG_SUM:
1347 case DTRACEAGG_COUNT:
1348 val = *addr;
1349 break;
1350
1351 case DTRACEAGG_AVG:
1352 val = addr[0] ? (addr[1] / addr[0]) : 0;
1353 break;
1354
1355 default:
1356 continue;
1357 }
1358
1359 if (total[agg->dtagd_varid] == NULL) {
1360 total[agg->dtagd_varid] = aggdata;
1361 aggdata->dtada_flags |= DTRACE_A_TOTAL;
1362 } else {
1363 aggdata = total[agg->dtagd_varid];
1364 }
1365
1366 if (val > 0)
1367 aggdata->dtada_flags |= DTRACE_A_HASPOSITIVES;
1368
1369 if (val < 0) {
1370 aggdata->dtada_flags |= DTRACE_A_HASNEGATIVES;
1371 val = -val;
1372 }
1373
1374 if (dtp->dt_options[DTRACEOPT_AGGZOOM] != DTRACEOPT_UNSET) {
1375 val = (int64_t)((long double)val *
1376 (1 / DTRACE_AGGZOOM_MAX));
1377
1378 if (val > aggdata->dtada_total)
1379 aggdata->dtada_total = val;
1380 } else {
1381 aggdata->dtada_total += val;
1382 }
1383 }
1384
1385 /*
1386 * And now one final pass to set everyone's total.
1387 */
1388 for (h = hash->dtah_all; h != NULL; h = h->dtahe_nextall) {
1389 dtrace_aggdata_t *aggdata = &h->dtahe_data, *t;
1390 dtrace_aggdesc_t *agg = aggdata->dtada_desc;
1391
1392 if ((t = total[agg->dtagd_varid]) == NULL || aggdata == t)
1393 continue;
1394
1395 aggdata->dtada_total = t->dtada_total;
1396 aggdata->dtada_flags |= (t->dtada_flags & tflags);
1397 }
1398
1399 dt_free(dtp, total);
1400
1401 return (0);
1402 }
1403
1404 static int
1405 dt_aggregate_minmaxbin(dtrace_hdl_t *dtp, boolean_t clear)
1406 {
1407 dt_ahashent_t *h;
1408 dtrace_aggdata_t **minmax;
1409 dtrace_aggid_t max = DTRACE_AGGVARIDNONE, id;
1410 dt_aggregate_t *agp = &dtp->dt_aggregate;
1411 dt_ahash_t *hash = &agp->dtat_hash;
1412
1413 for (h = hash->dtah_all; h != NULL; h = h->dtahe_nextall) {
1414 dtrace_aggdata_t *aggdata = &h->dtahe_data;
1415
1416 if ((id = dt_aggregate_aggvarid(h)) > max)
1417 max = id;
1418
1419 aggdata->dtada_minbin = 0;
1420 aggdata->dtada_maxbin = 0;
1421 aggdata->dtada_flags &= ~DTRACE_A_MINMAXBIN;
1422 }
1423
1424 if (clear || max == DTRACE_AGGVARIDNONE)
1425 return (0);
1426
1427 minmax = dt_zalloc(dtp, (max + 1) * sizeof (dtrace_aggdata_t *));
1428
1429 if (minmax == NULL)
1430 return (-1);
1431
1432 for (h = hash->dtah_all; h != NULL; h = h->dtahe_nextall) {
1433 dtrace_aggdata_t *aggdata = &h->dtahe_data;
1434 dtrace_aggdesc_t *agg = aggdata->dtada_desc;
1435 dtrace_recdesc_t *rec;
1436 caddr_t data;
1437 int64_t *addr;
1438 int minbin = -1, maxbin = -1, i;
1439 int start = 0, size;
1440
1441 rec = &agg->dtagd_rec[agg->dtagd_nrecs - 1];
1442 size = rec->dtrd_size / sizeof (int64_t);
1443 data = aggdata->dtada_data;
1444 addr = (int64_t *)(uintptr_t)(data + rec->dtrd_offset);
1445
1446 switch (rec->dtrd_action) {
1447 case DTRACEAGG_LQUANTIZE:
1448 /*
1449 * For lquantize(), we always display the entire range
1450 * of the aggregation when aggpack is set.
1451 */
1452 start = 1;
1453 minbin = start;
1454 maxbin = size - 1 - start;
1455 break;
1456
1457 case DTRACEAGG_QUANTIZE:
1458 for (i = start; i < size; i++) {
1459 if (!addr[i])
1460 continue;
1461
1462 if (minbin == -1)
1463 minbin = i - start;
1464
1465 maxbin = i - start;
1466 }
1467
1468 if (minbin == -1) {
1469 /*
1470 * If we have no data (e.g., due to a clear()
1471 * or negative increments), we'll use the
1472 * zero bucket as both our min and max.
1473 */
1474 minbin = maxbin = DTRACE_QUANTIZE_ZEROBUCKET;
1475 }
1476
1477 break;
1478
1479 default:
1480 continue;
1481 }
1482
1483 if (minmax[agg->dtagd_varid] == NULL) {
1484 minmax[agg->dtagd_varid] = aggdata;
1485 aggdata->dtada_flags |= DTRACE_A_MINMAXBIN;
1486 aggdata->dtada_minbin = minbin;
1487 aggdata->dtada_maxbin = maxbin;
1488 continue;
1489 }
1490
1491 if (minbin < minmax[agg->dtagd_varid]->dtada_minbin)
1492 minmax[agg->dtagd_varid]->dtada_minbin = minbin;
1493
1494 if (maxbin > minmax[agg->dtagd_varid]->dtada_maxbin)
1495 minmax[agg->dtagd_varid]->dtada_maxbin = maxbin;
1496 }
1497
1498 /*
1499 * And now one final pass to set everyone's minbin and maxbin.
1500 */
1501 for (h = hash->dtah_all; h != NULL; h = h->dtahe_nextall) {
1502 dtrace_aggdata_t *aggdata = &h->dtahe_data, *mm;
1503 dtrace_aggdesc_t *agg = aggdata->dtada_desc;
1504
1505 if ((mm = minmax[agg->dtagd_varid]) == NULL || aggdata == mm)
1506 continue;
1507
1508 aggdata->dtada_minbin = mm->dtada_minbin;
1509 aggdata->dtada_maxbin = mm->dtada_maxbin;
1510 aggdata->dtada_flags |= DTRACE_A_MINMAXBIN;
1511 }
1512
1513 dt_free(dtp, minmax);
1514
1515 return (0);
1516 }
1517
1518 static int
1519 dt_aggregate_walk_sorted(dtrace_hdl_t *dtp,
1520 dtrace_aggregate_f *func, void *arg,
1521 int (*sfunc)(const void *, const void *))
1522 {
1523 dt_aggregate_t *agp = &dtp->dt_aggregate;
1524 dt_ahashent_t *h, **sorted;
1525 dt_ahash_t *hash = &agp->dtat_hash;
1526 size_t i, nentries = 0;
1527 int rval = -1;
1528
1529 agp->dtat_flags &= ~(DTRACE_A_TOTAL | DTRACE_A_MINMAXBIN);
1530
1531 if (dtp->dt_options[DTRACEOPT_AGGHIST] != DTRACEOPT_UNSET) {
1532 agp->dtat_flags |= DTRACE_A_TOTAL;
1533
1534 if (dt_aggregate_total(dtp, B_FALSE) != 0)
1535 return (-1);
1536 }
1537
1538 if (dtp->dt_options[DTRACEOPT_AGGPACK] != DTRACEOPT_UNSET) {
1539 agp->dtat_flags |= DTRACE_A_MINMAXBIN;
1540
1541 if (dt_aggregate_minmaxbin(dtp, B_FALSE) != 0)
1542 return (-1);
1543 }
1544
1545 for (h = hash->dtah_all; h != NULL; h = h->dtahe_nextall)
1546 nentries++;
1547
1548 sorted = dt_alloc(dtp, nentries * sizeof (dt_ahashent_t *));
1549
1550 if (sorted == NULL)
1551 goto out;
1552
1553 for (h = hash->dtah_all, i = 0; h != NULL; h = h->dtahe_nextall)
1554 sorted[i++] = h;
1555
1556 (void) pthread_mutex_lock(&dt_qsort_lock);
1557
1558 if (sfunc == NULL) {
1559 dt_aggregate_qsort(dtp, sorted, nentries,
1560 sizeof (dt_ahashent_t *), NULL);
1561 } else {
1562 /*
1563 * If we've been explicitly passed a sorting function,
1564 * we'll use that -- ignoring the values of the "aggsortrev",
1565 * "aggsortkey" and "aggsortkeypos" options.
1566 */
1567 qsort(sorted, nentries, sizeof (dt_ahashent_t *), sfunc);
1568 }
1569
1570 (void) pthread_mutex_unlock(&dt_qsort_lock);
1571
1572 for (i = 0; i < nentries; i++) {
1573 h = sorted[i];
1574
1575 if (dt_aggwalk_rval(dtp, h, func(&h->dtahe_data, arg)) == -1)
1576 goto out;
1577 }
1578
1579 rval = 0;
1580 out:
1581 if (agp->dtat_flags & DTRACE_A_TOTAL)
1582 (void) dt_aggregate_total(dtp, B_TRUE);
1583
1584 if (agp->dtat_flags & DTRACE_A_MINMAXBIN)
1585 (void) dt_aggregate_minmaxbin(dtp, B_TRUE);
1586
1587 dt_free(dtp, sorted);
1588 return (rval);
1589 }
1590
1591 int
1592 dtrace_aggregate_walk_sorted(dtrace_hdl_t *dtp,
1593 dtrace_aggregate_f *func, void *arg)
1594 {
1595 return (dt_aggregate_walk_sorted(dtp, func, arg, NULL));
1596 }
1597
1598 int
1599 dtrace_aggregate_walk_keysorted(dtrace_hdl_t *dtp,
1600 dtrace_aggregate_f *func, void *arg)
1601 {
1602 return (dt_aggregate_walk_sorted(dtp, func,
1603 arg, dt_aggregate_varkeycmp));
1604 }
1605
1606 int
1607 dtrace_aggregate_walk_valsorted(dtrace_hdl_t *dtp,
1608 dtrace_aggregate_f *func, void *arg)
2091
2092 if (zaggdata != NULL) {
2093 for (i = 0; i < naggvars; i++)
2094 dt_free(dtp, zaggdata[i].dtahe_data.dtada_data);
2095 }
2096
2097 dt_free(dtp, zaggdata);
2098 dt_free(dtp, sorted);
2099 dt_free(dtp, remap);
2100 dt_free(dtp, map);
2101
2102 return (rval);
2103 }
2104
2105 int
2106 dtrace_aggregate_print(dtrace_hdl_t *dtp, FILE *fp,
2107 dtrace_aggregate_walk_f *func)
2108 {
2109 dt_print_aggdata_t pd;
2110
2111 bzero(&pd, sizeof (pd));
2112
2113 pd.dtpa_dtp = dtp;
2114 pd.dtpa_fp = fp;
2115 pd.dtpa_allunprint = 1;
2116
2117 if (func == NULL)
2118 func = dtrace_aggregate_walk_sorted;
2119
2120 if ((*func)(dtp, dt_print_agg, &pd) == -1)
2121 return (dt_set_errno(dtp, dtp->dt_errno));
2122
2123 return (0);
2124 }
2125
2126 void
2127 dtrace_aggregate_clear(dtrace_hdl_t *dtp)
2128 {
2129 dt_aggregate_t *agp = &dtp->dt_aggregate;
2130 dt_ahash_t *hash = &agp->dtat_hash;
2131 dt_ahashent_t *h;
2132 dtrace_aggdata_t *data;
|