static void string_stream_tests(void) { ph_string_t *str; ph_stream_t *stm; char buf[5]; uint64_t r; str = ph_string_make_empty(mt_misc, 16); stm = ph_stm_string_open(str); ok(stm, "made a stream"); ph_stm_printf(stm, "hello world"); ok(ph_string_equal_cstr(str, "hello world"), "see printf"); ok(ph_stm_seek(stm, 0, SEEK_SET, NULL), "rewound"); ok(ph_stm_read(stm, buf, sizeof(buf), &r), "read data"); is(r, sizeof(buf)); is(memcmp(buf, "hello", 5), 0); ph_stm_printf(stm, " kitty and append!"); ok(ph_string_equal_cstr(str, "hello kitty and append!"), "see printf"); ok(ph_stm_seek(stm, 6, SEEK_SET, NULL), "rewound"); ok(ph_stm_read(stm, buf, sizeof(buf), &r), "read data"); is(r, sizeof(buf)); is(memcmp(buf, "kitty", 5), 0); ok(ph_stm_seek(stm, -5, SEEK_END, NULL), "rewound"); ok(ph_stm_read(stm, buf, sizeof(buf), &r), "read data"); is(r, sizeof(buf)); is(memcmp(buf, "pend!", 5), 0); ph_stm_close(stm); ph_string_delref(str); }
int main(int argc, char **argv) { ph_stream_t *stm; char namebuf[128]; int fd; char buf[BUFSIZ]; uint64_t amount; int len; ph_unused_parameter(argc); ph_unused_parameter(argv); ph_library_init(); plan_tests(18); strcpy(namebuf, "/tmp/phenomXXXXXX"); fd = ph_mkostemp(namebuf, 0); diag("opened %s -> %d", namebuf, fd); unlink(namebuf); stm = ph_stm_fd_open(fd, 0, PH_STM_BUFSIZE); ph_stm_write(stm, "lemon\n", 6, &amount); is(amount, 6); // Shouldn't see it yet is(0, pread(fd, buf, sizeof(buf), 0)); // Should see it now ph_stm_flush(stm); is(6, pread(fd, buf, sizeof(buf), 0)); ok(!memcmp("lemon\n", buf, 6), "right content"); ok(ph_stm_seek(stm, 0, SEEK_SET, NULL), "seeked"); memset(buf, 0, sizeof(buf)); ok(ph_stm_read(stm, buf, 3, &amount), "read ok"); ok(amount == 3, "amount is %" PRIu64, amount); ok(!memcmp("lem", buf, 3), "got prefix"); ok(ph_stm_read(stm, buf, 3, &amount), "read ok"); ok(amount == 3, "amount is %" PRIu64, amount); ok(!memcmp("on\n", buf, 3), "got remainder"); ok(ph_stm_seek(stm, 0, SEEK_SET, NULL), "seeked"); len = ph_stm_printf(stm, "testing %d %s!", 1, "two"); ok(14 == len, "printed len %d", len); ok(ph_stm_seek(stm, 0, SEEK_SET, NULL), "seeked"); memset(buf, 0, sizeof(buf)); ok(ph_stm_read(stm, buf, 14, &amount), "read ok"); ok(amount == 14, "len was %" PRIu64, amount); ok(!memcmp("testing 1 two!", buf, 14), "got formatted"); ok(ph_stm_close(stm), "closed"); return exit_status(); }
// Query memory stats static void cmd_memory(ph_sock_t *sock) { ph_mem_stats_t stats[1]; ph_memtype_t base = PH_MEMTYPE_FIRST; char name[29]; ph_stm_printf(sock->stream, "%28s %9s %9s %9s %9s %9s\r\n", "WHAT", "BYTES", "OOM", "ALLOCS", "FREES", "REALLOC" ); while (1) { int n, i; n = ph_mem_stat_range(base, base + (sizeof(stats) / sizeof(stats[0])), stats); for (i = 0; i < n; i++) { ph_snprintf(name, sizeof(name), "%s/%s", stats[i].def->facility, stats[i].def->name); ph_stm_printf(sock->stream, "%28s " "%9"PRIu64" " "%9"PRIu64" " "%9"PRIu64" " "%9"PRIu64" " "%9"PRIu64" " "\r\n", name, stats[i].bytes, stats[i].oom, stats[i].allocs, stats[i].frees, stats[i].reallocs); } if ((uint32_t)n < sizeof(stats) / sizeof(stats[0])) { break; } base += n; } }
// Called each time the session wakes up. // The `why` parameter indicates why we were woken up static void echo_processor(ph_sock_t *sock, ph_iomask_t why, void *arg) { struct echo_state *state = arg; ph_buf_t *buf; // If the socket encountered an error, or if the timeout was reached // (there's a default timeout, even if we didn't override it), then // we tear down the session if (why & (PH_IOMASK_ERR|PH_IOMASK_TIME)) { ph_log(PH_LOG_ERR, "disconnecting `P{sockaddr:%p}", (void*)&sock->peername); ph_sock_shutdown(sock, PH_SOCK_SHUT_RDWR); ph_mem_free(mt_state, state); ph_sock_free(sock); return; } // We loop because echo_processor is only triggered by newly arriving // data or events from the kernel. If we have data buffered and only // partially consume it, we won't get woken up until the next data // arrives, if ever. while (1) { // Try to read a line of text. // This returns a slice over the underlying buffer (if the line was // smaller than a buffer) or a freshly made contiguous buffer (if the // line was larger than our buffer segment size). Either way, we // own a reference to the returned buffer and should treat it as // a read-only slice. buf = ph_sock_read_line(sock); if (!buf) { // Not available yet, we'll try again later return; } // We got a line; update our state state->num_lines++; // Send our response. The data is buffered and automatically sent // to the client as it becomes writable, so we don't need to handle // partial writes or EAGAIN here. // If this was a "real" server, we would still check the return value // from the writes and proceed to tear down the session if things failed. // Note that buf includes the trailing CRLF, so our response // will implicitly end with CRLF too. ph_stm_printf(sock->stream, "You said [%d]: ", state->num_lines); ph_stm_write(sock->stream, ph_buf_mem(buf), ph_buf_len(buf), NULL); // We're done with buf, so we must release it ph_buf_delref(buf); } }
// Query all counters except for memory counters. static void cmd_counters(ph_sock_t *sock) { #define NUM_SLOTS 64 #define NUM_COUNTERS 2048 struct counter_name_val counter_data[NUM_COUNTERS]; int64_t view_slots[NUM_SLOTS]; const char *view_names[NUM_SLOTS]; ph_counter_scope_iterator_t iter; uint32_t num_slots, i; uint32_t n_counters = 0; uint32_t longest_name = 0; char name[69]; // Collect all counter data; it is returned in an undefined order. // For the sake of testing we want to order it, so we collect the data // and then sort it ph_counter_scope_iterator_init(&iter); ph_counter_scope_t *iter_scope; while ((iter_scope = ph_counter_scope_iterator_next(&iter)) != NULL) { uint32_t slen; if (strncmp(ph_counter_scope_get_name(iter_scope), "memory/", 7) == 0) { ph_counter_scope_delref(iter_scope); continue; } slen = strlen(ph_counter_scope_get_name(iter_scope)); num_slots = ph_counter_scope_get_view(iter_scope, NUM_SLOTS, view_slots, view_names); for (i = 0; i < num_slots; i++) { uint32_t l = strlen(view_names[i]); longest_name = MAX(longest_name, l + slen + 1); counter_data[n_counters].scope_name = ph_counter_scope_get_name(iter_scope); counter_data[n_counters].name = view_names[i]; counter_data[n_counters].val = view_slots[i]; n_counters++; if (n_counters >= NUM_COUNTERS) { break; } } ph_counter_scope_delref(iter_scope); if (n_counters >= NUM_COUNTERS) { break; } } qsort(counter_data, n_counters, sizeof(struct counter_name_val), compare_counter_name_val); for (i = 0; i < n_counters; i++) { ph_snprintf(name, sizeof(name), "%s/%s", counter_data[i].scope_name, counter_data[i].name); ph_stm_printf(sock->stream, "%*s %16" PRIi64"\r\n", longest_name, name, counter_data[i].val); } if (n_counters >= NUM_COUNTERS) { ph_stm_printf(sock->stream, "WARNING: too many counters to sort, output truncated\r\n"); } }