|
|
@@ -36,6 +36,7 @@ extern "C" {
|
|
|
#include <fstream>
|
|
|
#include <sstream>
|
|
|
#include <regex>
|
|
|
+#include <iomanip>
|
|
|
|
|
|
/* Handlers */
|
|
|
zend_object_handlers bpf_object_handlers;
|
|
|
@@ -1297,15 +1298,211 @@ PHP_METHOD (HashTable, clear) {
|
|
|
}
|
|
|
|
|
|
PHP_METHOD (ArrayTable, get_value) {
|
|
|
- return;
|
|
|
+ zend_long index;
|
|
|
+ zval name_rv;
|
|
|
+ zval *name_zv;
|
|
|
+
|
|
|
+ if (zend_parse_parameters(ZEND_NUM_ARGS(), "l", &index) == FAILURE) {
|
|
|
+ RETURN_NULL();
|
|
|
+ }
|
|
|
+
|
|
|
+ name_zv = zend_read_property(
|
|
|
+ Z_OBJCE_P(getThis()),
|
|
|
+ getThis(),
|
|
|
+ "name",
|
|
|
+ sizeof("name") - 1,
|
|
|
+ 1,
|
|
|
+ &name_rv
|
|
|
+ );
|
|
|
+
|
|
|
+ if (!name_zv || Z_TYPE_P(name_zv) != IS_STRING) {
|
|
|
+ zend_throw_error(NULL, "Invalid or missing name property");
|
|
|
+ RETURN_NULL();
|
|
|
+ }
|
|
|
+
|
|
|
+ sub_object *obj = table_fetch_object(Z_OBJ_P(getThis()));
|
|
|
+ if (!obj || !obj->bpf) {
|
|
|
+ zend_throw_error(NULL, "Invalid object state");
|
|
|
+ RETURN_NULL();
|
|
|
+ }
|
|
|
+
|
|
|
+ try {
|
|
|
+ auto table = obj->bpf->get_array_table<uint64_t>(Z_STRVAL_P(name_zv));
|
|
|
+ uint64_t val;
|
|
|
+ auto res = table.get_value(index, val);
|
|
|
+ if (!res.ok()) {
|
|
|
+ zend_throw_error(NULL, "Get value error in %s", Z_STRVAL_P(name_zv));
|
|
|
+ RETURN_NULL();
|
|
|
+ }
|
|
|
+ RETURN_LONG(val);
|
|
|
+ } catch (const std::exception &e) {
|
|
|
+ zend_throw_error(NULL, "Exception: %s", e.what());
|
|
|
+ RETURN_NULL();
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
PHP_METHOD (ArrayTable, print_log2_hist) {
|
|
|
- return;
|
|
|
+ char *header;
|
|
|
+ size_t header_len;
|
|
|
+ zval name_rv;
|
|
|
+ zval *name_zv;
|
|
|
+
|
|
|
+ if (zend_parse_parameters(ZEND_NUM_ARGS(), "s", &header, &header_len) == FAILURE) {
|
|
|
+ RETURN_NULL();
|
|
|
+ }
|
|
|
+
|
|
|
+ name_zv = zend_read_property(
|
|
|
+ Z_OBJCE_P(getThis()),
|
|
|
+ getThis(),
|
|
|
+ "name",
|
|
|
+ sizeof("name") - 1,
|
|
|
+ 1,
|
|
|
+ &name_rv
|
|
|
+ );
|
|
|
+
|
|
|
+ if (!name_zv || Z_TYPE_P(name_zv) != IS_STRING) {
|
|
|
+ zend_throw_error(NULL, "Invalid or missing name property");
|
|
|
+ RETURN_NULL();
|
|
|
+ }
|
|
|
+
|
|
|
+ sub_object *obj = table_fetch_object(Z_OBJ_P(getThis()));
|
|
|
+ if (!obj || !obj->bpf) {
|
|
|
+ zend_throw_error(NULL, "Invalid object state");
|
|
|
+ RETURN_NULL();
|
|
|
+ }
|
|
|
+
|
|
|
+ auto table = obj->bpf->get_array_table<uint64_t>(Z_STRVAL_P(name_zv));
|
|
|
+
|
|
|
+ auto vals = table.get_table_offline();
|
|
|
+ auto center_text = [](const std::string &text, int width) -> std::string {
|
|
|
+ int len = static_cast<int>(text.length());
|
|
|
+ if (width <= len) return text;
|
|
|
+ int padding = width - len;
|
|
|
+ int left = padding / 2;
|
|
|
+ int right = padding - left;
|
|
|
+ return std::string(left, ' ') + text + std::string(right, ' ');
|
|
|
+ };
|
|
|
+ auto stars = [](uint64_t val, uint64_t max_val, int width) -> std::string {
|
|
|
+ std::string result;
|
|
|
+ int limit = std::min(width, static_cast<int>((double) val * width / max_val));
|
|
|
+ for (int i = 0; i < limit; ++i) {
|
|
|
+ result += '*';
|
|
|
+ }
|
|
|
+ if (val > max_val && !result.empty()) {
|
|
|
+ result.back() = '+';
|
|
|
+ }
|
|
|
+ return result;
|
|
|
+ };
|
|
|
+
|
|
|
+ int idx_max = -1;
|
|
|
+ uint64_t val_max = 0;
|
|
|
+
|
|
|
+ for (size_t i = 0; i < vals.size(); ++i) {
|
|
|
+ if (vals[i] > 0) {
|
|
|
+ idx_max = static_cast<int>(i);
|
|
|
+ if (vals[i] > val_max) val_max = vals[i];
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ if (idx_max == -1) {
|
|
|
+ std::cout << "No data to display." << std::endl;
|
|
|
+ RETURN_FALSE
|
|
|
+ }
|
|
|
+
|
|
|
+ int stars_max = 40;
|
|
|
+ bool long_format = idx_max > 32;
|
|
|
+
|
|
|
+ int col1_width = long_format ? 41 : 27;
|
|
|
+ int label_width = col1_width - 2;
|
|
|
+
|
|
|
+ std::cout << center_text(header, label_width) << ": count distribution" << std::endl;
|
|
|
+
|
|
|
+ bool strip_leading_zero = true;
|
|
|
+ for (int i = 1; i <= idx_max; ++i) {
|
|
|
+ uint64_t low = (1ULL << (i - 1));
|
|
|
+ uint64_t high = (1ULL << i) - 1;
|
|
|
+ if (low == high) low -= 1;
|
|
|
+
|
|
|
+ uint64_t val = vals[i];
|
|
|
+ if (strip_leading_zero && val == 0) {
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+ strip_leading_zero = false;
|
|
|
+
|
|
|
+ std::string bar = stars(val, val_max, stars_max);
|
|
|
+ if (long_format) {
|
|
|
+ std::cout << std::right << std::setw(20) << low << " -> "
|
|
|
+ << std::left << std::setw(20) << high << " : "
|
|
|
+ << std::setw(8) << val << "|" << bar << "|" << std::endl;
|
|
|
+ } else {
|
|
|
+ std::cout << std::right << std::setw(10) << low << " -> "
|
|
|
+ << std::left << std::setw(10) << high << " : "
|
|
|
+ << std::setw(8) << val << "|" << bar << "|" << std::endl;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ RETURN_FALSE
|
|
|
}
|
|
|
|
|
|
PHP_METHOD (ArrayTable, print_linear_hist) {
|
|
|
- return;
|
|
|
+ char *header;
|
|
|
+ size_t header_len;
|
|
|
+ zval name_rv;
|
|
|
+ zval *name_zv;
|
|
|
+
|
|
|
+ if (zend_parse_parameters(ZEND_NUM_ARGS(), "s", &header, &header_len) == FAILURE) {
|
|
|
+ RETURN_NULL();
|
|
|
+ }
|
|
|
+
|
|
|
+ name_zv = zend_read_property(
|
|
|
+ Z_OBJCE_P(getThis()),
|
|
|
+ getThis(),
|
|
|
+ "name",
|
|
|
+ sizeof("name") - 1,
|
|
|
+ 1,
|
|
|
+ &name_rv
|
|
|
+ );
|
|
|
+
|
|
|
+ if (!name_zv || Z_TYPE_P(name_zv) != IS_STRING) {
|
|
|
+ zend_throw_error(NULL, "Invalid or missing name property");
|
|
|
+ RETURN_NULL();
|
|
|
+ }
|
|
|
+
|
|
|
+ sub_object *obj = table_fetch_object(Z_OBJ_P(getThis()));
|
|
|
+ if (!obj || !obj->bpf) {
|
|
|
+ zend_throw_error(NULL, "Invalid object state");
|
|
|
+ RETURN_NULL();
|
|
|
+ }
|
|
|
+
|
|
|
+ auto table = obj->bpf->get_array_table<uint64_t>(Z_STRVAL_P(name_zv));
|
|
|
+ auto res = table.get_table_offline();
|
|
|
+
|
|
|
+ if (res.empty()) {
|
|
|
+ std::cout << "empty histogram" << std::endl;
|
|
|
+ RETURN_FALSE
|
|
|
+ }
|
|
|
+
|
|
|
+ uint64_t max_val = *std::max_element(res.begin(), res.end());
|
|
|
+ if (max_val == 0) max_val = 1;
|
|
|
+ std::cout << " " << std::left << std::setw(12) << header
|
|
|
+ << std::setw(10) << ": count"
|
|
|
+ << " distribution\n";
|
|
|
+
|
|
|
+
|
|
|
+ int stars_max = 40;
|
|
|
+ for (size_t i = 0; i < res.size(); ++i) {
|
|
|
+ if (res[i] == 0) continue;
|
|
|
+
|
|
|
+ std::string bar;
|
|
|
+ int limit = std::min(stars_max, static_cast<int>((double) res[i] * stars_max / max_val));
|
|
|
+ for (int j = 0; j < limit; ++j) bar += '*';
|
|
|
+ if (res[i] > max_val && !bar.empty()) bar.back() = '+';
|
|
|
+
|
|
|
+ std::string count_str = ": " + std::to_string(res[i]);
|
|
|
+ std::cout << " " << std::left << std::setw(12) << i
|
|
|
+ << std::setw(10) << count_str
|
|
|
+ << "|" << bar << "|\n";
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
PHP_METHOD (PerCpuArrayTable, sum_value) {
|