/* * Copyright (C) 1996-2024 The Squid Software Foundation and contributors * * Squid software is distributed under GPLv2+ license and includes * contributions from numerous individuals and organizations. * Please see the COPYING and CONTRIBUTORS files for details. */ /* DEBUG: section 18 Cache Manager Statistics */ #include "squid.h" #include "AccessLogEntry.h" #include "CacheDigest.h" #include "CachePeer.h" #include "client_side.h" #include "client_side_request.h" #include "comm/Connection.h" #include "comm/Loops.h" #include "event.h" #include "fde.h" #include "format/Token.h" #include "globals.h" #include "http/Stream.h" #include "HttpRequest.h" #include "IoStats.h" #include "mem/Pool.h" #include "mem/Stats.h" #include "mem_node.h" #include "MemBuf.h" #include "MemObject.h" #include "mgr/CountersAction.h" #include "mgr/FunAction.h" #include "mgr/InfoAction.h" #include "mgr/IntervalAction.h" #include "mgr/IoAction.h" #include "mgr/Registration.h" #include "mgr/ServiceTimesAction.h" #include "neighbors.h" #include "PeerDigest.h" #include "SquidConfig.h" #include "SquidMath.h" #include "stat.h" #include "StatCounters.h" #include "Store.h" #include "store_digest.h" #include "StoreClient.h" #include "tools.h" #if USE_AUTH #include "auth/UserRequest.h" #endif #if USE_DELAY_POOLS #include "DelayId.h" #endif #if USE_OPENSSL #include "ssl/support.h" #endif /* these are included because they expose stats calls */ /* TODO: provide a self registration mechanism for those classes * to use during static construction */ #include "comm.h" #include "StoreSearch.h" typedef int STOBJFLT(const StoreEntry *); class StatObjectsState { CBDATA_CLASS(StatObjectsState); public: StoreEntry *sentry; STOBJFLT *filter; StoreSearchPointer theSearch; }; /* LOCALS */ static const char *describeStatuses(const StoreEntry *); static void statAvgTick(void *notused); static void statAvgDump(StoreEntry *, int minutes, int hours); #if STAT_GRAPHS static void statGraphDump(StoreEntry *); #endif static double statPctileSvc(double, int, int); static void statStoreEntry(MemBuf * mb, StoreEntry * e); static double statCPUUsage(int minutes); static OBJH stat_objects_get; static OBJH stat_vmobjects_get; static OBJH statOpenfdObj; static EVH statObjects; static OBJH statCountersDump; static OBJH statPeerSelect; static OBJH statDigestBlob; static OBJH statUtilization; static OBJH statCountersHistograms; static OBJH statClientRequests; void GetAvgStat(Mgr::IntervalActionData& stats, int minutes, int hours); void DumpAvgStat(Mgr::IntervalActionData& stats, StoreEntry* sentry); void GetInfo(Mgr::InfoActionData& stats); void DumpInfo(Mgr::InfoActionData& stats, StoreEntry* sentry); void DumpMallocStatistics(StoreEntry* sentry); void GetCountersStats(Mgr::CountersActionData& stats); void DumpCountersStats(Mgr::CountersActionData& stats, StoreEntry* sentry); void GetServiceTimesStats(Mgr::ServiceTimesActionData& stats); void DumpServiceTimesStats(Mgr::ServiceTimesActionData& stats, StoreEntry* sentry); void GetIoStats(Mgr::IoActionData& stats); void DumpIoStats(Mgr::IoActionData& stats, StoreEntry* sentry); #if XMALLOC_STATISTICS static void info_get_mallstat(int, int, int, void *); static double xm_time; static double xm_deltat; #endif StatCounters CountHist[N_COUNT_HIST]; static int NCountHist = 0; static StatCounters CountHourHist[N_COUNT_HOUR_HIST]; static int NCountHourHist = 0; CBDATA_CLASS_INIT(StatObjectsState); extern unsigned int mem_pool_alloc_calls; extern unsigned int mem_pool_free_calls; static void statUtilization(StoreEntry * e) { storeAppendPrintf(e, "Cache Utilisation:\n"); storeAppendPrintf(e, "\n"); storeAppendPrintf(e, "Last 5 minutes:\n"); if (NCountHist >= 5) statAvgDump(e, 5, 0); else storeAppendPrintf(e, "(no values recorded yet)\n"); storeAppendPrintf(e, "\n"); storeAppendPrintf(e, "Last 15 minutes:\n"); if (NCountHist >= 15) statAvgDump(e, 15, 0); else storeAppendPrintf(e, "(no values recorded yet)\n"); storeAppendPrintf(e, "\n"); storeAppendPrintf(e, "Last hour:\n"); if (NCountHist >= 60) statAvgDump(e, 60, 0); else storeAppendPrintf(e, "(no values recorded yet)\n"); storeAppendPrintf(e, "\n"); storeAppendPrintf(e, "Last 8 hours:\n"); if (NCountHourHist >= 8) statAvgDump(e, 0, 8); else storeAppendPrintf(e, "(no values recorded yet)\n"); storeAppendPrintf(e, "\n"); storeAppendPrintf(e, "Last day:\n"); if (NCountHourHist >= 24) statAvgDump(e, 0, 24); else storeAppendPrintf(e, "(no values recorded yet)\n"); storeAppendPrintf(e, "\n"); storeAppendPrintf(e, "Last 3 days:\n"); if (NCountHourHist >= 72) statAvgDump(e, 0, 72); else storeAppendPrintf(e, "(no values recorded yet)\n"); storeAppendPrintf(e, "\n"); storeAppendPrintf(e, "Totals since cache startup:\n"); statCountersDump(e); } void GetIoStats(Mgr::IoActionData& stats) { int i; stats.http_reads = IOStats.Http.reads; for (i = 0; i < IoStats::histSize; ++i) { stats.http_read_hist[i] = IOStats.Http.read_hist[i]; } stats.ftp_reads = IOStats.Ftp.reads; for (i = 0; i < IoStats::histSize; ++i) { stats.ftp_read_hist[i] = IOStats.Ftp.read_hist[i]; } } void DumpIoStats(Mgr::IoActionData& stats, StoreEntry* sentry) { int i; storeAppendPrintf(sentry, "HTTP I/O\n"); storeAppendPrintf(sentry, "number of reads: %.0f\n", stats.http_reads); storeAppendPrintf(sentry, "Read Histogram:\n"); for (i = 0; i < IoStats::histSize; ++i) { storeAppendPrintf(sentry, "%5d-%5d: %9.0f %2.0f%%\n", i ? (1 << (i - 1)) + 1 : 1, 1 << i, stats.http_read_hist[i], Math::doublePercent(stats.http_read_hist[i], stats.http_reads)); } storeAppendPrintf(sentry, "\n"); storeAppendPrintf(sentry, "FTP I/O\n"); storeAppendPrintf(sentry, "number of reads: %.0f\n", stats.ftp_reads); storeAppendPrintf(sentry, "Read Histogram:\n"); for (i = 0; i < IoStats::histSize; ++i) { storeAppendPrintf(sentry, "%5d-%5d: %9.0f %2.0f%%\n", i ? (1 << (i - 1)) + 1 : 1, 1 << i, stats.ftp_read_hist[i], Math::doublePercent(stats.ftp_read_hist[i], stats.ftp_reads)); } storeAppendPrintf(sentry, "\n"); } static const char * describeStatuses(const StoreEntry * entry) { LOCAL_ARRAY(char, buf, 256); snprintf(buf, 256, "%-13s %-13s %-12s %-12s", storeStatusStr[entry->store_status], memStatusStr[entry->mem_status], swapStatusStr[entry->swap_status], pingStatusStr[entry->ping_status]); return buf; } const char * storeEntryFlags(const StoreEntry * entry) { LOCAL_ARRAY(char, buf, 256); int flags = (int) entry->flags; char *t; buf[0] = '\0'; if (EBIT_TEST(flags, ENTRY_SPECIAL)) strcat(buf, "SPECIAL,"); if (EBIT_TEST(flags, ENTRY_REVALIDATE_ALWAYS)) strcat(buf, "REVALIDATE_ALWAYS,"); if (EBIT_TEST(flags, DELAY_SENDING)) strcat(buf, "DELAY_SENDING,"); if (EBIT_TEST(flags, RELEASE_REQUEST)) strcat(buf, "RELEASE_REQUEST,"); if (EBIT_TEST(flags, REFRESH_REQUEST)) strcat(buf, "REFRESH_REQUEST,"); if (EBIT_TEST(flags, ENTRY_REVALIDATE_STALE)) strcat(buf, "REVALIDATE_STALE,"); if (EBIT_TEST(flags, ENTRY_DISPATCHED)) strcat(buf, "DISPATCHED,"); if (EBIT_TEST(flags, KEY_PRIVATE)) strcat(buf, "PRIVATE,"); if (EBIT_TEST(flags, ENTRY_FWD_HDR_WAIT)) strcat(buf, "FWD_HDR_WAIT,"); if (EBIT_TEST(flags, ENTRY_NEGCACHED)) strcat(buf, "NEGCACHED,"); if (EBIT_TEST(flags, ENTRY_VALIDATED)) strcat(buf, "VALIDATED,"); if (EBIT_TEST(flags, ENTRY_BAD_LENGTH)) strcat(buf, "BAD_LENGTH,"); if (EBIT_TEST(flags, ENTRY_ABORTED)) strcat(buf, "ABORTED,"); if ((t = strrchr(buf, ','))) *t = '\0'; return buf; } static void statStoreEntry(MemBuf * mb, StoreEntry * e) { MemObject *mem = e->mem_obj; mb->appendf("KEY %s\n", e->getMD5Text()); mb->appendf("\t%s\n", describeStatuses(e)); mb->appendf("\t%s\n", storeEntryFlags(e)); mb->appendf("\t%s\n", e->describeTimestamps()); mb->appendf("\t%d locks, %d clients, %d refs\n", (int) e->locks(), storePendingNClients(e), (int) e->refcount); mb->appendf("\tSwap Dir %d, File %#08X\n", e->swap_dirn, e->swap_filen); if (mem != nullptr) mem->stat (mb); mb->append("\n", 1); } /* process objects list */ static void statObjects(void *data) { StatObjectsState *state = static_cast(data); StoreEntry *e; if (state->theSearch->isDone()) { if (UsingSmp()) storeAppendPrintf(state->sentry, "} by kid%d\n\n", KidIdentifier); state->sentry->complete(); state->sentry->unlock("statObjects+isDone"); delete state; return; } else if (EBIT_TEST(state->sentry->flags, ENTRY_ABORTED)) { state->sentry->unlock("statObjects+aborted"); delete state; return; } else if (state->sentry->checkDeferRead(-1)) { state->sentry->flush(); eventAdd("statObjects", statObjects, state, 0.1, 1); return; } state->sentry->buffer(); size_t statCount = 0; MemBuf mb; mb.init(); while (statCount++ < static_cast(Config.Store.objectsPerBucket) && state-> theSearch->next()) { e = state->theSearch->currentItem(); if (state->filter && 0 == state->filter(e)) continue; statStoreEntry(&mb, e); } if (mb.size) state->sentry->append(mb.buf, mb.size); mb.clean(); eventAdd("statObjects", statObjects, state, 0.0, 1); } static void statObjectsStart(StoreEntry * sentry, STOBJFLT * filter) { StatObjectsState *state = new StatObjectsState; state->sentry = sentry; state->filter = filter; sentry->lock("statObjects"); state->theSearch = Store::Root().search(); eventAdd("statObjects", statObjects, state, 0.0, 1); } static void stat_objects_get(StoreEntry * sentry) { statObjectsStart(sentry, nullptr); } static int statObjectsVmFilter(const StoreEntry * e) { return e->mem_obj ? 1 : 0; } static void stat_vmobjects_get(StoreEntry * sentry) { statObjectsStart(sentry, statObjectsVmFilter); } static int statObjectsOpenfdFilter(const StoreEntry * e) { if (e->mem_obj == nullptr) return 0; if (e->mem_obj->swapout.sio == nullptr) return 0; return 1; } static void statOpenfdObj(StoreEntry * sentry) { statObjectsStart(sentry, statObjectsOpenfdFilter); } #if XMALLOC_STATISTICS static void info_get_mallstat(int size, int number, int oldnum, void *data) { StoreEntry *sentry = (StoreEntry *)data; // format: "%12s %15s %6s %12s\n","Alloc Size","Count","Delta","Alloc/sec" if (number > 0) storeAppendPrintf(sentry, "%12d %15d %6d %.1f\n", size, number, number - oldnum, xdiv((number - oldnum), xm_deltat)); } #endif void GetInfo(Mgr::InfoActionData& stats) { struct rusage rusage; double cputime; double runtime; #if HAVE_MSTATS && HAVE_GNUMALLOC_H struct mstats ms; #endif runtime = tvSubDsec(squid_start, current_time); if (runtime == 0.0) runtime = 1.0; stats.squid_start = squid_start; stats.current_time = current_time; stats.client_http_clients = statCounter.client_http.clients; stats.client_http_requests = statCounter.client_http.requests; stats.icp_pkts_recv = statCounter.icp.pkts_recv; stats.icp_pkts_sent = statCounter.icp.pkts_sent; stats.icp_replies_queued = statCounter.icp.replies_queued; #if USE_HTCP stats.htcp_pkts_recv = statCounter.htcp.pkts_recv; stats.htcp_pkts_sent = statCounter.htcp.pkts_sent; #endif stats.request_failure_ratio = request_failure_ratio; stats.avg_client_http_requests = statCounter.client_http.requests / (runtime / 60.0); stats.avg_icp_messages = (statCounter.icp.pkts_sent + statCounter.icp.pkts_recv) / (runtime / 60.0); stats.select_loops = statCounter.select_loops; stats.avg_loop_time = 1000.0 * runtime / statCounter.select_loops; stats.request_hit_ratio5 = statRequestHitRatio(5); stats.request_hit_ratio60 = statRequestHitRatio(60); stats.byte_hit_ratio5 = statByteHitRatio(5); stats.byte_hit_ratio60 = statByteHitRatio(60); stats.request_hit_mem_ratio5 = statRequestHitMemoryRatio(5); stats.request_hit_mem_ratio60 = statRequestHitMemoryRatio(60); stats.request_hit_disk_ratio5 = statRequestHitDiskRatio(5); stats.request_hit_disk_ratio60 = statRequestHitDiskRatio(60); Store::Root().getStats(stats.store); stats.unlink_requests = statCounter.unlink.requests; stats.http_requests5 = statPctileSvc(0.5, 5, PCTILE_HTTP); stats.http_requests60 = statPctileSvc(0.5, 60, PCTILE_HTTP); stats.cache_misses5 = statPctileSvc(0.5, 5, PCTILE_MISS); stats.cache_misses60 = statPctileSvc(0.5, 60, PCTILE_MISS); stats.cache_hits5 = statPctileSvc(0.5, 5, PCTILE_HIT); stats.cache_hits60 = statPctileSvc(0.5, 60, PCTILE_HIT); stats.near_hits5 = statPctileSvc(0.5, 5, PCTILE_NH); stats.near_hits60 = statPctileSvc(0.5, 60, PCTILE_NH); stats.not_modified_replies5 = statPctileSvc(0.5, 5, PCTILE_NM); stats.not_modified_replies60 = statPctileSvc(0.5, 60, PCTILE_NM); stats.dns_lookups5 = statPctileSvc(0.5, 5, PCTILE_DNS); stats.dns_lookups60 = statPctileSvc(0.5, 60, PCTILE_DNS); stats.icp_queries5 = statPctileSvc(0.5, 5, PCTILE_ICP_QUERY); stats.icp_queries60 = statPctileSvc(0.5, 60, PCTILE_ICP_QUERY); squid_getrusage(&rusage); cputime = rusage_cputime(&rusage); stats.up_time = runtime; stats.cpu_time = cputime; stats.cpu_usage = Math::doublePercent(cputime, runtime); stats.cpu_usage5 = statCPUUsage(5); stats.cpu_usage60 = statCPUUsage(60); stats.maxrss = rusage_maxrss(&rusage); stats.page_faults = rusage_pagefaults(&rusage); #if HAVE_MSTATS && HAVE_GNUMALLOC_H ms = mstats(); stats.ms_bytes_total = ms.bytes_total; stats.ms_bytes_free = ms.bytes_free; #endif { Mem::PoolStats mp_stats; Mem::GlobalStats(mp_stats); stats.gb_saved_count = mp_stats.meter->gb_saved.count; stats.gb_freed_count = mp_stats.meter->gb_freed.count; stats.total_accounted = mp_stats.meter->alloc.currentLevel(); } stats.max_fd = Squid_MaxFD; stats.biggest_fd = Biggest_FD; stats.number_fd = Number_FD; stats.opening_fd = Opening_FD; stats.num_fd_free = fdNFree(); stats.reserved_fd = RESERVED_FD; } void DumpInfo(Mgr::InfoActionData& stats, StoreEntry* sentry) { storeAppendPrintf(sentry, "Squid Object Cache: Version %s\n", version_string); storeAppendPrintf(sentry, "Build Info: " SQUID_BUILD_INFO "\n"); #if _SQUID_WINDOWS_ if (WIN32_run_mode == _WIN_SQUID_RUN_MODE_SERVICE) { storeAppendPrintf(sentry,"\nRunning as " SQUIDSBUFPH " Windows System Service on %s\n", SQUIDSBUFPRINT(service_name), WIN32_OS_string); storeAppendPrintf(sentry,"Service command line is: %s\n", WIN32_Service_Command_Line); } else storeAppendPrintf(sentry,"Running on %s\n",WIN32_OS_string); #else storeAppendPrintf(sentry,"Service Name: " SQUIDSBUFPH "\n", SQUIDSBUFPRINT(service_name)); #endif storeAppendPrintf(sentry, "Start Time:\t%s\n", Time::FormatRfc1123(stats.squid_start.tv_sec)); storeAppendPrintf(sentry, "Current Time:\t%s\n", Time::FormatRfc1123(stats.current_time.tv_sec)); storeAppendPrintf(sentry, "Connection information for %s:\n",APP_SHORTNAME); if (Config.onoff.client_db) storeAppendPrintf(sentry, "\tNumber of clients accessing cache:\t%.0f\n", stats.client_http_clients); else sentry->append("\tNumber of clients accessing cache:\t(client_db off)\n", 52); storeAppendPrintf(sentry, "\tNumber of HTTP requests received:\t%.0f\n", stats.client_http_requests); storeAppendPrintf(sentry, "\tNumber of ICP messages received:\t%.0f\n", stats.icp_pkts_recv); storeAppendPrintf(sentry, "\tNumber of ICP messages sent:\t%.0f\n", stats.icp_pkts_sent); storeAppendPrintf(sentry, "\tNumber of queued ICP replies:\t%.0f\n", stats.icp_replies_queued); #if USE_HTCP storeAppendPrintf(sentry, "\tNumber of HTCP messages received:\t%.0f\n", stats.htcp_pkts_recv); storeAppendPrintf(sentry, "\tNumber of HTCP messages sent:\t%.0f\n", stats.htcp_pkts_sent); #endif double fct = stats.count > 1 ? stats.count : 1.0; storeAppendPrintf(sentry, "\tRequest failure ratio:\t%5.2f\n", stats.request_failure_ratio / fct); storeAppendPrintf(sentry, "\tAverage HTTP requests per minute since start:\t%.1f\n", stats.avg_client_http_requests); storeAppendPrintf(sentry, "\tAverage ICP messages per minute since start:\t%.1f\n", stats.avg_icp_messages); storeAppendPrintf(sentry, "\tSelect loop called: %.0f times, %0.3f ms avg\n", stats.select_loops, stats.avg_loop_time / fct); storeAppendPrintf(sentry, "Cache information for %s:\n",APP_SHORTNAME); storeAppendPrintf(sentry, "\tHits as %% of all requests:\t5min: %3.1f%%, 60min: %3.1f%%\n", stats.request_hit_ratio5 / fct, stats.request_hit_ratio60 / fct); storeAppendPrintf(sentry, "\tHits as %% of bytes sent:\t5min: %3.1f%%, 60min: %3.1f%%\n", stats.byte_hit_ratio5 / fct, stats.byte_hit_ratio60 / fct); storeAppendPrintf(sentry, "\tMemory hits as %% of hit requests:\t5min: %3.1f%%, 60min: %3.1f%%\n", stats.request_hit_mem_ratio5 / fct, stats.request_hit_mem_ratio60 / fct); storeAppendPrintf(sentry, "\tDisk hits as %% of hit requests:\t5min: %3.1f%%, 60min: %3.1f%%\n", stats.request_hit_disk_ratio5 / fct, stats.request_hit_disk_ratio60 / fct); storeAppendPrintf(sentry, "\tStorage Swap size:\t%.0f KB\n", stats.store.swap.size / 1024); storeAppendPrintf(sentry, "\tStorage Swap capacity:\t%4.1f%% used, %4.1f%% free\n", Math::doublePercent(stats.store.swap.size, stats.store.swap.capacity), Math::doublePercent(stats.store.swap.available(), stats.store.swap.capacity)); storeAppendPrintf(sentry, "\tStorage Mem size:\t%.0f KB\n", stats.store.mem.size / 1024); storeAppendPrintf(sentry, "\tStorage Mem capacity:\t%4.1f%% used, %4.1f%% free\n", Math::doublePercent(stats.store.mem.size, stats.store.mem.capacity), Math::doublePercent(stats.store.mem.available(), stats.store.mem.capacity)); storeAppendPrintf(sentry, "\tMean Object Size:\t%0.2f KB\n", stats.store.swap.meanObjectSize() / 1024); storeAppendPrintf(sentry, "\tRequests given to unlinkd:\t%.0f\n", stats.unlink_requests); storeAppendPrintf(sentry, "Median Service Times (seconds) 5 min 60 min:\n"); fct = stats.count > 1 ? stats.count * 1000.0 : 1000.0; storeAppendPrintf(sentry, "\tHTTP Requests (All): %8.5f %8.5f\n", stats.http_requests5 / fct, stats.http_requests60 / fct); storeAppendPrintf(sentry, "\tCache Misses: %8.5f %8.5f\n", stats.cache_misses5 / fct, stats.cache_misses60 / fct); storeAppendPrintf(sentry, "\tCache Hits: %8.5f %8.5f\n", stats.cache_hits5 / fct, stats.cache_hits60 / fct); storeAppendPrintf(sentry, "\tNear Hits: %8.5f %8.5f\n", stats.near_hits5 / fct, stats.near_hits60 / fct); storeAppendPrintf(sentry, "\tNot-Modified Replies: %8.5f %8.5f\n", stats.not_modified_replies5 / fct, stats.not_modified_replies60 / fct); storeAppendPrintf(sentry, "\tDNS Lookups: %8.5f %8.5f\n", stats.dns_lookups5 / fct, stats.dns_lookups60 / fct); fct = stats.count > 1 ? stats.count * 1000000.0 : 1000000.0; storeAppendPrintf(sentry, "\tICP Queries: %8.5f %8.5f\n", stats.icp_queries5 / fct, stats.icp_queries60 / fct); storeAppendPrintf(sentry, "Resource usage for %s:\n", APP_SHORTNAME); storeAppendPrintf(sentry, "\tUP Time:\t%.3f seconds\n", stats.up_time); storeAppendPrintf(sentry, "\tCPU Time:\t%.3f seconds\n", stats.cpu_time); storeAppendPrintf(sentry, "\tCPU Usage:\t%.2f%%\n", stats.cpu_usage); storeAppendPrintf(sentry, "\tCPU Usage, 5 minute avg:\t%.2f%%\n", stats.cpu_usage5); storeAppendPrintf(sentry, "\tCPU Usage, 60 minute avg:\t%.2f%%\n", stats.cpu_usage60); storeAppendPrintf(sentry, "\tMaximum Resident Size: %.0f KB\n", stats.maxrss); storeAppendPrintf(sentry, "\tPage faults with physical i/o: %.0f\n", stats.page_faults); #if HAVE_MSTATS && HAVE_GNUMALLOC_H storeAppendPrintf(sentry, "Memory usage for %s via mstats():\n",APP_SHORTNAME); storeAppendPrintf(sentry, "\tTotal space in arena: %6.0f KB\n", stats.ms_bytes_total / 1024); storeAppendPrintf(sentry, "\tTotal free: %6.0f KB %.0f%%\n", stats.ms_bytes_free / 1024, Math::doublePercent(stats.ms_bytes_free, stats.ms_bytes_total)); #endif storeAppendPrintf(sentry, "Memory accounted for:\n"); storeAppendPrintf(sentry, "\tTotal accounted: %6.0f KB\n", stats.total_accounted / 1024); { Mem::PoolStats mp_stats; Mem::GlobalStats(mp_stats); // XXX: called just for its side effects storeAppendPrintf(sentry, "\tmemPoolAlloc calls: %9.0f\n", stats.gb_saved_count); storeAppendPrintf(sentry, "\tmemPoolFree calls: %9.0f\n", stats.gb_freed_count); } storeAppendPrintf(sentry, "File descriptor usage for %s:\n", APP_SHORTNAME); storeAppendPrintf(sentry, "\tMaximum number of file descriptors: %4.0f\n", stats.max_fd); storeAppendPrintf(sentry, "\tLargest file desc currently in use: %4.0f\n", stats.biggest_fd); storeAppendPrintf(sentry, "\tNumber of file desc currently in use: %4.0f\n", stats.number_fd); storeAppendPrintf(sentry, "\tFiles queued for open: %4.0f\n", stats.opening_fd); storeAppendPrintf(sentry, "\tAvailable number of file descriptors: %4.0f\n", stats.num_fd_free); storeAppendPrintf(sentry, "\tReserved number of file descriptors: %4.0f\n", stats.reserved_fd); storeAppendPrintf(sentry, "\tStore Disk files open: %4.0f\n", stats.store.swap.open_disk_fd); storeAppendPrintf(sentry, "Internal Data Structures:\n"); storeAppendPrintf(sentry, "\t%6.0f StoreEntries\n", stats.store.store_entry_count); storeAppendPrintf(sentry, "\t%6.0f StoreEntries with MemObjects\n", stats.store.mem_object_count); storeAppendPrintf(sentry, "\t%6.0f Hot Object Cache Items\n", stats.store.mem.count); storeAppendPrintf(sentry, "\t%6.0f on-disk objects\n", stats.store.swap.count); } void DumpMallocStatistics(StoreEntry* sentry) { #if XMALLOC_STATISTICS xm_deltat = current_dtime - xm_time; xm_time = current_dtime; storeAppendPrintf(sentry, "\nMemory allocation statistics\n"); storeAppendPrintf(sentry, "%12s %15s %6s %12s\n","Alloc Size","Count","Delta","Alloc/sec"); malloc_statistics(info_get_mallstat, sentry); #else (void)sentry; #endif } void GetServiceTimesStats(Mgr::ServiceTimesActionData& stats) { for (int i = 0; i < Mgr::ServiceTimesActionData::seriesSize; ++i) { double p = (i + 1) * 5 / 100.0; stats.http_requests5[i] = statPctileSvc(p, 5, PCTILE_HTTP); stats.http_requests60[i] = statPctileSvc(p, 60, PCTILE_HTTP); stats.cache_misses5[i] = statPctileSvc(p, 5, PCTILE_MISS); stats.cache_misses60[i] = statPctileSvc(p, 60, PCTILE_MISS); stats.cache_hits5[i] = statPctileSvc(p, 5, PCTILE_HIT); stats.cache_hits60[i] = statPctileSvc(p, 60, PCTILE_HIT); stats.near_hits5[i] = statPctileSvc(p, 5, PCTILE_NH); stats.near_hits60[i] = statPctileSvc(p, 60, PCTILE_NH); stats.not_modified_replies5[i] = statPctileSvc(p, 5, PCTILE_NM); stats.not_modified_replies60[i] = statPctileSvc(p, 60, PCTILE_NM); stats.dns_lookups5[i] = statPctileSvc(p, 5, PCTILE_DNS); stats.dns_lookups60[i] = statPctileSvc(p, 60, PCTILE_DNS); stats.icp_queries5[i] = statPctileSvc(p, 5, PCTILE_ICP_QUERY); stats.icp_queries60[i] = statPctileSvc(p, 60, PCTILE_ICP_QUERY); } } void DumpServiceTimesStats(Mgr::ServiceTimesActionData& stats, StoreEntry* sentry) { storeAppendPrintf(sentry, "Service Time Percentiles 5 min 60 min:\n"); double fct = stats.count > 1 ? stats.count * 1000.0 : 1000.0; for (int i = 0; i < Mgr::ServiceTimesActionData::seriesSize; ++i) { storeAppendPrintf(sentry, "\tHTTP Requests (All): %2d%% %8.5f %8.5f\n", (i + 1) * 5, stats.http_requests5[i] / fct, stats.http_requests60[i] / fct); } for (int i = 0; i < Mgr::ServiceTimesActionData::seriesSize; ++i) { storeAppendPrintf(sentry, "\tCache Misses: %2d%% %8.5f %8.5f\n", (i + 1) * 5, stats.cache_misses5[i] / fct, stats.cache_misses60[i] / fct); } for (int i = 0; i < Mgr::ServiceTimesActionData::seriesSize; ++i) { storeAppendPrintf(sentry, "\tCache Hits: %2d%% %8.5f %8.5f\n", (i + 1) * 5, stats.cache_hits5[i] / fct, stats.cache_hits60[i] / fct); } for (int i = 0; i < Mgr::ServiceTimesActionData::seriesSize; ++i) { storeAppendPrintf(sentry, "\tNear Hits: %2d%% %8.5f %8.5f\n", (i + 1) * 5, stats.near_hits5[i] / fct, stats.near_hits60[i] / fct); } for (int i = 0; i < Mgr::ServiceTimesActionData::seriesSize; ++i) { storeAppendPrintf(sentry, "\tNot-Modified Replies: %2d%% %8.5f %8.5f\n", (i + 1) * 5, stats.not_modified_replies5[i] / fct, stats.not_modified_replies60[i] / fct); } for (int i = 0; i < Mgr::ServiceTimesActionData::seriesSize; ++i) { storeAppendPrintf(sentry, "\tDNS Lookups: %2d%% %8.5f %8.5f\n", (i + 1) * 5, stats.dns_lookups5[i] / fct, stats.dns_lookups60[i] / fct); } fct = stats.count > 1 ? stats.count * 1000000.0 : 1000000.0; for (int i = 0; i < Mgr::ServiceTimesActionData::seriesSize; ++i) { storeAppendPrintf(sentry, "\tICP Queries: %2d%% %8.5f %8.5f\n", (i + 1) * 5, stats.icp_queries5[i] / fct, stats.icp_queries60[i] / fct); } } static void statAvgDump(StoreEntry * sentry, int minutes, int hours) { Mgr::IntervalActionData stats; GetAvgStat(stats, minutes, hours); DumpAvgStat(stats, sentry); } #define XAVG(X) (dt ? (double) (f->X - l->X) / dt : 0.0) void GetAvgStat(Mgr::IntervalActionData& stats, int minutes, int hours) { StatCounters *f; StatCounters *l; double dt; double ct; assert(N_COUNT_HIST > 1); assert(minutes > 0 || hours > 0); f = &CountHist[0]; l = f; if (minutes > 0 && hours == 0) { /* checking minute readings ... */ if (minutes > N_COUNT_HIST - 1) minutes = N_COUNT_HIST - 1; l = &CountHist[minutes]; } else if (minutes == 0 && hours > 0) { /* checking hour readings ... */ if (hours > N_COUNT_HOUR_HIST - 1) hours = N_COUNT_HOUR_HIST - 1; l = &CountHourHist[hours]; } else { debugs(18, DBG_IMPORTANT, "ERROR: statAvgDump: Invalid args, minutes=" << minutes << ", hours=" << hours); return; } dt = tvSubDsec(l->timestamp, f->timestamp); ct = f->cputime - l->cputime; stats.sample_start_time = l->timestamp; stats.sample_end_time = f->timestamp; stats.client_http_requests = XAVG(client_http.requests); stats.client_http_hits = XAVG(client_http.hits); stats.client_http_errors = XAVG(client_http.errors); stats.client_http_kbytes_in = XAVG(client_http.kbytes_in.kb); stats.client_http_kbytes_out = XAVG(client_http.kbytes_out.kb); stats.client_http_all_median_svc_time = statHistDeltaMedian(l->client_http.allSvcTime, f->client_http.allSvcTime) / 1000.0; stats.client_http_miss_median_svc_time = statHistDeltaMedian(l->client_http.missSvcTime, f->client_http.missSvcTime) / 1000.0; stats.client_http_nm_median_svc_time = statHistDeltaMedian(l->client_http.nearMissSvcTime, f->client_http.nearMissSvcTime) / 1000.0; stats.client_http_nh_median_svc_time = statHistDeltaMedian(l->client_http.nearHitSvcTime, f->client_http.nearHitSvcTime) / 1000.0; stats.client_http_hit_median_svc_time = statHistDeltaMedian(l->client_http.hitSvcTime, f->client_http.hitSvcTime) / 1000.0; stats.server_all_requests = XAVG(server.all.requests); stats.server_all_errors = XAVG(server.all.errors); stats.server_all_kbytes_in = XAVG(server.all.kbytes_in.kb); stats.server_all_kbytes_out = XAVG(server.all.kbytes_out.kb); stats.server_http_requests = XAVG(server.http.requests); stats.server_http_errors = XAVG(server.http.errors); stats.server_http_kbytes_in = XAVG(server.http.kbytes_in.kb); stats.server_http_kbytes_out = XAVG(server.http.kbytes_out.kb); stats.server_ftp_requests = XAVG(server.ftp.requests); stats.server_ftp_errors = XAVG(server.ftp.errors); stats.server_ftp_kbytes_in = XAVG(server.ftp.kbytes_in.kb); stats.server_ftp_kbytes_out = XAVG(server.ftp.kbytes_out.kb); stats.server_other_requests = XAVG(server.other.requests); stats.server_other_errors = XAVG(server.other.errors); stats.server_other_kbytes_in = XAVG(server.other.kbytes_in.kb); stats.server_other_kbytes_out = XAVG(server.other.kbytes_out.kb); stats.icp_pkts_sent = XAVG(icp.pkts_sent); stats.icp_pkts_recv = XAVG(icp.pkts_recv); stats.icp_queries_sent = XAVG(icp.queries_sent); stats.icp_replies_sent = XAVG(icp.replies_sent); stats.icp_queries_recv = XAVG(icp.queries_recv); stats.icp_replies_recv = XAVG(icp.replies_recv); stats.icp_replies_queued = XAVG(icp.replies_queued); stats.icp_query_timeouts = XAVG(icp.query_timeouts); stats.icp_kbytes_sent = XAVG(icp.kbytes_sent.kb); stats.icp_kbytes_recv = XAVG(icp.kbytes_recv.kb); stats.icp_q_kbytes_sent = XAVG(icp.q_kbytes_sent.kb); stats.icp_r_kbytes_sent = XAVG(icp.r_kbytes_sent.kb); stats.icp_q_kbytes_recv = XAVG(icp.q_kbytes_recv.kb); stats.icp_r_kbytes_recv = XAVG(icp.r_kbytes_recv.kb); stats.icp_query_median_svc_time = statHistDeltaMedian(l->icp.querySvcTime, f->icp.querySvcTime) / 1000000.0; stats.icp_reply_median_svc_time = statHistDeltaMedian(l->icp.replySvcTime, f->icp.replySvcTime) / 1000000.0; stats.dns_median_svc_time = statHistDeltaMedian(l->dns.svcTime, f->dns.svcTime) / 1000.0; stats.unlink_requests = XAVG(unlink.requests); stats.page_faults = XAVG(page_faults); stats.select_loops = XAVG(select_loops); stats.select_fds = XAVG(select_fds); stats.average_select_fd_period = f->select_fds > l->select_fds ? (f->select_time - l->select_time) / (f->select_fds - l->select_fds) : 0.0; stats.median_select_fds = statHistDeltaMedian(l->select_fds_hist, f->select_fds_hist); stats.swap_outs = XAVG(swap.outs); stats.swap_ins = XAVG(swap.ins); stats.swap_files_cleaned = XAVG(swap.files_cleaned); stats.aborted_requests = XAVG(aborted_requests); stats.hitValidationAttempts = XAVG(hitValidation.attempts); stats.hitValidationRefusalsDueToLocking = XAVG(hitValidation.refusalsDueToLocking); stats.hitValidationRefusalsDueToZeroSize = XAVG(hitValidation.refusalsDueToZeroSize); stats.hitValidationRefusalsDueToTimeLimit = XAVG(hitValidation.refusalsDueToTimeLimit); stats.hitValidationFailures = XAVG(hitValidation.failures); stats.syscalls_disk_opens = XAVG(syscalls.disk.opens); stats.syscalls_disk_closes = XAVG(syscalls.disk.closes); stats.syscalls_disk_reads = XAVG(syscalls.disk.reads); stats.syscalls_disk_writes = XAVG(syscalls.disk.writes); stats.syscalls_disk_seeks = XAVG(syscalls.disk.seeks); stats.syscalls_disk_unlinks = XAVG(syscalls.disk.unlinks); stats.syscalls_sock_accepts = XAVG(syscalls.sock.accepts); stats.syscalls_sock_sockets = XAVG(syscalls.sock.sockets); stats.syscalls_sock_connects = XAVG(syscalls.sock.connects); stats.syscalls_sock_binds = XAVG(syscalls.sock.binds); stats.syscalls_sock_closes = XAVG(syscalls.sock.closes); stats.syscalls_sock_reads = XAVG(syscalls.sock.reads); stats.syscalls_sock_writes = XAVG(syscalls.sock.writes); stats.syscalls_sock_recvfroms = XAVG(syscalls.sock.recvfroms); stats.syscalls_sock_sendtos = XAVG(syscalls.sock.sendtos); stats.syscalls_selects = XAVG(syscalls.selects); stats.cpu_time = ct; stats.wall_time = dt; } void DumpAvgStat(Mgr::IntervalActionData& stats, StoreEntry* sentry) { storeAppendPrintf(sentry, "sample_start_time = %d.%d (%s)\n", (int)stats.sample_start_time.tv_sec, (int)stats.sample_start_time.tv_usec, Time::FormatRfc1123(stats.sample_start_time.tv_sec)); storeAppendPrintf(sentry, "sample_end_time = %d.%d (%s)\n", (int)stats.sample_end_time.tv_sec, (int)stats.sample_end_time.tv_usec, Time::FormatRfc1123(stats.sample_end_time.tv_sec)); storeAppendPrintf(sentry, "client_http.requests = %f/sec\n", stats.client_http_requests); storeAppendPrintf(sentry, "client_http.hits = %f/sec\n", stats.client_http_hits); storeAppendPrintf(sentry, "client_http.errors = %f/sec\n", stats.client_http_errors); storeAppendPrintf(sentry, "client_http.kbytes_in = %f/sec\n", stats.client_http_kbytes_in); storeAppendPrintf(sentry, "client_http.kbytes_out = %f/sec\n", stats.client_http_kbytes_out); double fct = stats.count > 1 ? stats.count : 1.0; storeAppendPrintf(sentry, "client_http.all_median_svc_time = %f seconds\n", stats.client_http_all_median_svc_time / fct); storeAppendPrintf(sentry, "client_http.miss_median_svc_time = %f seconds\n", stats.client_http_miss_median_svc_time / fct); storeAppendPrintf(sentry, "client_http.nm_median_svc_time = %f seconds\n", stats.client_http_nm_median_svc_time / fct); storeAppendPrintf(sentry, "client_http.nh_median_svc_time = %f seconds\n", stats.client_http_nh_median_svc_time / fct); storeAppendPrintf(sentry, "client_http.hit_median_svc_time = %f seconds\n", stats.client_http_hit_median_svc_time / fct); storeAppendPrintf(sentry, "server.all.requests = %f/sec\n", stats.server_all_requests); storeAppendPrintf(sentry, "server.all.errors = %f/sec\n", stats.server_all_errors); storeAppendPrintf(sentry, "server.all.kbytes_in = %f/sec\n", stats.server_all_kbytes_in); storeAppendPrintf(sentry, "server.all.kbytes_out = %f/sec\n", stats.server_all_kbytes_out); storeAppendPrintf(sentry, "server.http.requests = %f/sec\n", stats.server_http_requests); storeAppendPrintf(sentry, "server.http.errors = %f/sec\n", stats.server_http_errors); storeAppendPrintf(sentry, "server.http.kbytes_in = %f/sec\n", stats.server_http_kbytes_in); storeAppendPrintf(sentry, "server.http.kbytes_out = %f/sec\n", stats.server_http_kbytes_out); storeAppendPrintf(sentry, "server.ftp.requests = %f/sec\n", stats.server_ftp_requests); storeAppendPrintf(sentry, "server.ftp.errors = %f/sec\n", stats.server_ftp_errors); storeAppendPrintf(sentry, "server.ftp.kbytes_in = %f/sec\n", stats.server_ftp_kbytes_in); storeAppendPrintf(sentry, "server.ftp.kbytes_out = %f/sec\n", stats.server_ftp_kbytes_out); storeAppendPrintf(sentry, "server.other.requests = %f/sec\n", stats.server_other_requests); storeAppendPrintf(sentry, "server.other.errors = %f/sec\n", stats.server_other_errors); storeAppendPrintf(sentry, "server.other.kbytes_in = %f/sec\n", stats.server_other_kbytes_in); storeAppendPrintf(sentry, "server.other.kbytes_out = %f/sec\n", stats.server_other_kbytes_out); storeAppendPrintf(sentry, "icp.pkts_sent = %f/sec\n", stats.icp_pkts_sent); storeAppendPrintf(sentry, "icp.pkts_recv = %f/sec\n", stats.icp_pkts_recv); storeAppendPrintf(sentry, "icp.queries_sent = %f/sec\n", stats.icp_queries_sent); storeAppendPrintf(sentry, "icp.replies_sent = %f/sec\n", stats.icp_replies_sent); storeAppendPrintf(sentry, "icp.queries_recv = %f/sec\n", stats.icp_queries_recv); storeAppendPrintf(sentry, "icp.replies_recv = %f/sec\n", stats.icp_replies_recv); storeAppendPrintf(sentry, "icp.replies_queued = %f/sec\n", stats.icp_replies_queued); storeAppendPrintf(sentry, "icp.query_timeouts = %f/sec\n", stats.icp_query_timeouts); storeAppendPrintf(sentry, "icp.kbytes_sent = %f/sec\n", stats.icp_kbytes_sent); storeAppendPrintf(sentry, "icp.kbytes_recv = %f/sec\n", stats.icp_kbytes_recv); storeAppendPrintf(sentry, "icp.q_kbytes_sent = %f/sec\n", stats.icp_q_kbytes_sent); storeAppendPrintf(sentry, "icp.r_kbytes_sent = %f/sec\n", stats.icp_r_kbytes_sent); storeAppendPrintf(sentry, "icp.q_kbytes_recv = %f/sec\n", stats.icp_q_kbytes_recv); storeAppendPrintf(sentry, "icp.r_kbytes_recv = %f/sec\n", stats.icp_r_kbytes_recv); storeAppendPrintf(sentry, "icp.query_median_svc_time = %f seconds\n", stats.icp_query_median_svc_time / fct); storeAppendPrintf(sentry, "icp.reply_median_svc_time = %f seconds\n", stats.icp_reply_median_svc_time / fct); storeAppendPrintf(sentry, "dns.median_svc_time = %f seconds\n", stats.dns_median_svc_time / fct); storeAppendPrintf(sentry, "unlink.requests = %f/sec\n", stats.unlink_requests); storeAppendPrintf(sentry, "page_faults = %f/sec\n", stats.page_faults); storeAppendPrintf(sentry, "select_loops = %f/sec\n", stats.select_loops); storeAppendPrintf(sentry, "select_fds = %f/sec\n", stats.select_fds); storeAppendPrintf(sentry, "average_select_fd_period = %f/fd\n", stats.average_select_fd_period / fct); storeAppendPrintf(sentry, "median_select_fds = %f\n", stats.median_select_fds / fct); storeAppendPrintf(sentry, "swap.outs = %f/sec\n", stats.swap_outs); storeAppendPrintf(sentry, "swap.ins = %f/sec\n", stats.swap_ins); storeAppendPrintf(sentry, "swap.files_cleaned = %f/sec\n", stats.swap_files_cleaned); storeAppendPrintf(sentry, "aborted_requests = %f/sec\n", stats.aborted_requests); storeAppendPrintf(sentry, "hit_validation.attempts = %f/sec\n", stats.hitValidationAttempts); storeAppendPrintf(sentry, "hit_validation.refusals.due_to_locking = %f/sec\n", stats.hitValidationRefusalsDueToLocking); storeAppendPrintf(sentry, "hit_validation.refusals.due_to_zeroSize = %f/sec\n", stats.hitValidationRefusalsDueToZeroSize); storeAppendPrintf(sentry, "hit_validation.refusals.due_to_timeLimit = %f/sec\n", stats.hitValidationRefusalsDueToTimeLimit); storeAppendPrintf(sentry, "hit_validation.failures = %f/sec\n", stats.hitValidationFailures); #if USE_POLL storeAppendPrintf(sentry, "syscalls.polls = %f/sec\n", stats.syscalls_selects); #elif defined(USE_SELECT) || defined(USE_SELECT_WIN32) storeAppendPrintf(sentry, "syscalls.selects = %f/sec\n", stats.syscalls_selects); #endif storeAppendPrintf(sentry, "syscalls.disk.opens = %f/sec\n", stats.syscalls_disk_opens); storeAppendPrintf(sentry, "syscalls.disk.closes = %f/sec\n", stats.syscalls_disk_closes); storeAppendPrintf(sentry, "syscalls.disk.reads = %f/sec\n", stats.syscalls_disk_reads); storeAppendPrintf(sentry, "syscalls.disk.writes = %f/sec\n", stats.syscalls_disk_writes); storeAppendPrintf(sentry, "syscalls.disk.seeks = %f/sec\n", stats.syscalls_disk_seeks); storeAppendPrintf(sentry, "syscalls.disk.unlinks = %f/sec\n", stats.syscalls_disk_unlinks); storeAppendPrintf(sentry, "syscalls.sock.accepts = %f/sec\n", stats.syscalls_sock_accepts); storeAppendPrintf(sentry, "syscalls.sock.sockets = %f/sec\n", stats.syscalls_sock_sockets); storeAppendPrintf(sentry, "syscalls.sock.connects = %f/sec\n", stats.syscalls_sock_connects); storeAppendPrintf(sentry, "syscalls.sock.binds = %f/sec\n", stats.syscalls_sock_binds); storeAppendPrintf(sentry, "syscalls.sock.closes = %f/sec\n", stats.syscalls_sock_closes); storeAppendPrintf(sentry, "syscalls.sock.reads = %f/sec\n", stats.syscalls_sock_reads); storeAppendPrintf(sentry, "syscalls.sock.writes = %f/sec\n", stats.syscalls_sock_writes); storeAppendPrintf(sentry, "syscalls.sock.recvfroms = %f/sec\n", stats.syscalls_sock_recvfroms); storeAppendPrintf(sentry, "syscalls.sock.sendtos = %f/sec\n", stats.syscalls_sock_sendtos); storeAppendPrintf(sentry, "cpu_time = %f seconds\n", stats.cpu_time); storeAppendPrintf(sentry, "wall_time = %f seconds\n", stats.wall_time); storeAppendPrintf(sentry, "cpu_usage = %f%%\n", Math::doublePercent(stats.cpu_time, stats.wall_time)); } static void statRegisterWithCacheManager(void) { Mgr::RegisterAction("info", "General Runtime Information", &Mgr::InfoAction::Create, 0, 1); Mgr::RegisterAction("service_times", "Service Times (Percentiles)", &Mgr::ServiceTimesAction::Create, 0, 1); Mgr::RegisterAction("filedescriptors", "Process Filedescriptor Allocation", fde::DumpStats, 0, 1); Mgr::RegisterAction("objects", "All Cache Objects", stat_objects_get, 0, 0); Mgr::RegisterAction("vm_objects", "In-Memory and In-Transit Objects", stat_vmobjects_get, 0, 0); Mgr::RegisterAction("io", "Server-side network read() size histograms", &Mgr::IoAction::Create, 0, 1); Mgr::RegisterAction("counters", "Traffic and Resource Counters", &Mgr::CountersAction::Create, 0, 1); Mgr::RegisterAction("peer_select", "Peer Selection Algorithms", statPeerSelect, 0, 1); Mgr::RegisterAction("digest_stats", "Cache Digest and ICP blob", statDigestBlob, 0, 1); Mgr::RegisterAction("5min", "5 Minute Average of Counters", &Mgr::IntervalAction::Create5min, 0, 1); Mgr::RegisterAction("60min", "60 Minute Average of Counters", &Mgr::IntervalAction::Create60min, 0, 1); Mgr::RegisterAction("utilization", "Cache Utilization", statUtilization, 0, 1); Mgr::RegisterAction("histograms", "Full Histogram Counts", statCountersHistograms, 0, 1); Mgr::RegisterAction("active_requests", "Client-side Active Requests", statClientRequests, 0, 1); #if USE_AUTH Mgr::RegisterAction("username_cache", "Active Cached Usernames", Auth::User::CredentialsCacheStats, 0, 1); #endif Mgr::RegisterAction("openfd_objects", "Objects with Swapout files open", statOpenfdObj, 0, 0); #if STAT_GRAPHS Mgr::RegisterAction("graph_variables", "Display cache metrics graphically", statGraphDump, 0, 1); #endif } /* add special cases here as they arrive */ static void statCountersInitSpecial(StatCounters * C) { /* * HTTP svc_time hist is kept in milli-seconds; max of 3 hours. */ C->client_http.allSvcTime.logInit(300, 0.0, 3600000.0 * 3.0); C->client_http.missSvcTime.logInit(300, 0.0, 3600000.0 * 3.0); C->client_http.nearMissSvcTime.logInit(300, 0.0, 3600000.0 * 3.0); C->client_http.nearHitSvcTime.logInit(300, 0.0, 3600000.0 * 3.0); C->client_http.hitSvcTime.logInit(300, 0.0, 3600000.0 * 3.0); /* * ICP svc_time hist is kept in micro-seconds; max of 1 minute. */ C->icp.querySvcTime.logInit(300, 0.0, 1000000.0 * 60.0); C->icp.replySvcTime.logInit(300, 0.0, 1000000.0 * 60.0); /* * DNS svc_time hist is kept in milli-seconds; max of 10 minutes. */ C->dns.svcTime.logInit(300, 0.0, 60000.0 * 10.0); /* * Cache Digest Stuff */ C->cd.on_xition_count.enumInit(CacheDigestHashFuncCount); C->comm_udp_incoming.enumInit(INCOMING_UDP_MAX); C->comm_dns_incoming.enumInit(INCOMING_DNS_MAX); C->comm_tcp_incoming.enumInit(INCOMING_TCP_MAX); C->select_fds_hist.enumInit(256); /* was SQUID_MAXFD, but it is way too much. It is OK to crop this statistics */ } static void statCountersInit(StatCounters * C) { assert(C); *C = StatCounters(); statCountersInitSpecial(C); } void statInit(void) { int i; debugs(18, 5, "statInit: Initializing..."); for (i = 0; i < N_COUNT_HIST; ++i) statCountersInit(&CountHist[i]); for (i = 0; i < N_COUNT_HOUR_HIST; ++i) statCountersInit(&CountHourHist[i]); statCountersInit(&statCounter); eventAdd("statAvgTick", statAvgTick, nullptr, (double) COUNT_INTERVAL, 1); ClientActiveRequests.head = nullptr; ClientActiveRequests.tail = nullptr; statRegisterWithCacheManager(); } static void statAvgTick(void *) { struct rusage rusage; eventAdd("statAvgTick", statAvgTick, nullptr, (double) COUNT_INTERVAL, 1); squid_getrusage(&rusage); statCounter.page_faults = rusage_pagefaults(&rusage); statCounter.cputime = rusage_cputime(&rusage); statCounter.timestamp = current_time; // shift all elements right and prepend statCounter for(int i = N_COUNT_HIST-1; i > 0; --i) CountHist[i] = CountHist[i-1]; CountHist[0] = statCounter; ++NCountHist; if ((NCountHist % COUNT_INTERVAL) == 0) { /* we have an hours worth of readings. store previous hour */ // shift all elements right and prepend final CountHist element for(int i = N_COUNT_HOUR_HIST-1; i > 0; --i) CountHourHist[i] = CountHourHist[i-1]; CountHourHist[0] = CountHist[N_COUNT_HIST - 1]; ++NCountHourHist; } if (Config.warnings.high_rptm > 0) { int i = (int) statPctileSvc(0.5, 20, PCTILE_HTTP); if (Config.warnings.high_rptm < i) debugs(18, DBG_CRITICAL, "WARNING: Median response time is " << i << " milliseconds"); } if (Config.warnings.high_pf) { int i = (CountHist[0].page_faults - CountHist[1].page_faults); double dt = tvSubDsec(CountHist[0].timestamp, CountHist[1].timestamp); if (i > 0 && dt > 0.0) { i /= (int) dt; if (Config.warnings.high_pf < i) debugs(18, DBG_CRITICAL, "WARNING: Page faults occurring at " << i << "/sec"); } } if (Config.warnings.high_memory) { size_t i = 0; #if HAVE_MSTATS && HAVE_GNUMALLOC_H struct mstats ms = mstats(); i = ms.bytes_total; #endif if (Config.warnings.high_memory < i) debugs(18, DBG_CRITICAL, "WARNING: Memory usage at " << ((unsigned long int)(i >> 20)) << " MB"); } } static void statCountersHistograms(StoreEntry * sentry) { storeAppendPrintf(sentry, "client_http.allSvcTime histogram:\n"); statCounter.client_http.allSvcTime.dump(sentry, nullptr); storeAppendPrintf(sentry, "client_http.missSvcTime histogram:\n"); statCounter.client_http.missSvcTime.dump(sentry, nullptr); storeAppendPrintf(sentry, "client_http.nearMissSvcTime histogram:\n"); statCounter.client_http.nearMissSvcTime.dump(sentry, nullptr); storeAppendPrintf(sentry, "client_http.nearHitSvcTime histogram:\n"); statCounter.client_http.nearHitSvcTime.dump(sentry, nullptr); storeAppendPrintf(sentry, "client_http.hitSvcTime histogram:\n"); statCounter.client_http.hitSvcTime.dump(sentry, nullptr); storeAppendPrintf(sentry, "icp.querySvcTime histogram:\n"); statCounter.icp.querySvcTime.dump(sentry, nullptr); storeAppendPrintf(sentry, "icp.replySvcTime histogram:\n"); statCounter.icp.replySvcTime.dump(sentry, nullptr); storeAppendPrintf(sentry, "dns.svc_time histogram:\n"); statCounter.dns.svcTime.dump(sentry, nullptr); storeAppendPrintf(sentry, "select_fds_hist histogram:\n"); statCounter.select_fds_hist.dump(sentry, nullptr); } static void statCountersDump(StoreEntry * sentry) { Mgr::CountersActionData stats; GetCountersStats(stats); DumpCountersStats(stats, sentry); } void GetCountersStats(Mgr::CountersActionData& stats) { StatCounters *f = &statCounter; struct rusage rusage; squid_getrusage(&rusage); f->page_faults = rusage_pagefaults(&rusage); f->cputime = rusage_cputime(&rusage); stats.sample_time = f->timestamp; stats.client_http_requests = f->client_http.requests; stats.client_http_hits = f->client_http.hits; stats.client_http_errors = f->client_http.errors; stats.client_http_kbytes_in = f->client_http.kbytes_in.kb; stats.client_http_kbytes_out = f->client_http.kbytes_out.kb; stats.client_http_hit_kbytes_out = f->client_http.hit_kbytes_out.kb; stats.server_all_requests = f->server.all.requests; stats.server_all_errors = f->server.all.errors; stats.server_all_kbytes_in = f->server.all.kbytes_in.kb; stats.server_all_kbytes_out = f->server.all.kbytes_out.kb; stats.server_http_requests = f->server.http.requests; stats.server_http_errors = f->server.http.errors; stats.server_http_kbytes_in = f->server.http.kbytes_in.kb; stats.server_http_kbytes_out = f->server.http.kbytes_out.kb; stats.server_ftp_requests = f->server.ftp.requests; stats.server_ftp_errors = f->server.ftp.errors; stats.server_ftp_kbytes_in = f->server.ftp.kbytes_in.kb; stats.server_ftp_kbytes_out = f->server.ftp.kbytes_out.kb; stats.server_other_requests = f->server.other.requests; stats.server_other_errors = f->server.other.errors; stats.server_other_kbytes_in = f->server.other.kbytes_in.kb; stats.server_other_kbytes_out = f->server.other.kbytes_out.kb; stats.icp_pkts_sent = f->icp.pkts_sent; stats.icp_pkts_recv = f->icp.pkts_recv; stats.icp_queries_sent = f->icp.queries_sent; stats.icp_replies_sent = f->icp.replies_sent; stats.icp_queries_recv = f->icp.queries_recv; stats.icp_replies_recv = f->icp.replies_recv; stats.icp_query_timeouts = f->icp.query_timeouts; stats.icp_replies_queued = f->icp.replies_queued; stats.icp_kbytes_sent = f->icp.kbytes_sent.kb; stats.icp_kbytes_recv = f->icp.kbytes_recv.kb; stats.icp_q_kbytes_sent = f->icp.q_kbytes_sent.kb; stats.icp_r_kbytes_sent = f->icp.r_kbytes_sent.kb; stats.icp_q_kbytes_recv = f->icp.q_kbytes_recv.kb; stats.icp_r_kbytes_recv = f->icp.r_kbytes_recv.kb; #if USE_CACHE_DIGESTS stats.icp_times_used = f->icp.times_used; stats.cd_times_used = f->cd.times_used; stats.cd_msgs_sent = f->cd.msgs_sent; stats.cd_msgs_recv = f->cd.msgs_recv; stats.cd_memory = f->cd.memory.kb; stats.cd_local_memory = store_digest ? store_digest->mask_size / 1024 : 0; stats.cd_kbytes_sent = f->cd.kbytes_sent.kb; stats.cd_kbytes_recv = f->cd.kbytes_recv.kb; #endif stats.unlink_requests = f->unlink.requests; stats.page_faults = f->page_faults; stats.select_loops = f->select_loops; stats.cpu_time = f->cputime; stats.wall_time = tvSubDsec(f->timestamp, current_time); stats.swap_outs = f->swap.outs; stats.swap_ins = f->swap.ins; stats.swap_files_cleaned = f->swap.files_cleaned; stats.aborted_requests = f->aborted_requests; stats.hitValidationAttempts = f->hitValidation.attempts; stats.hitValidationRefusalsDueToLocking = f->hitValidation.refusalsDueToLocking; stats.hitValidationRefusalsDueToZeroSize = f->hitValidation.refusalsDueToZeroSize; stats.hitValidationRefusalsDueToTimeLimit = f->hitValidation.refusalsDueToTimeLimit; stats.hitValidationFailures = f->hitValidation.failures; } void DumpCountersStats(Mgr::CountersActionData& stats, StoreEntry* sentry) { storeAppendPrintf(sentry, "sample_time = %d.%d (%s)\n", (int) stats.sample_time.tv_sec, (int) stats.sample_time.tv_usec, Time::FormatRfc1123(stats.sample_time.tv_sec)); storeAppendPrintf(sentry, "client_http.requests = %.0f\n", stats.client_http_requests); storeAppendPrintf(sentry, "client_http.hits = %.0f\n", stats.client_http_hits); storeAppendPrintf(sentry, "client_http.errors = %.0f\n", stats.client_http_errors); storeAppendPrintf(sentry, "client_http.kbytes_in = %.0f\n", stats.client_http_kbytes_in); storeAppendPrintf(sentry, "client_http.kbytes_out = %.0f\n", stats.client_http_kbytes_out); storeAppendPrintf(sentry, "client_http.hit_kbytes_out = %.0f\n", stats.client_http_hit_kbytes_out); storeAppendPrintf(sentry, "server.all.requests = %.0f\n", stats.server_all_requests); storeAppendPrintf(sentry, "server.all.errors = %.0f\n", stats.server_all_errors); storeAppendPrintf(sentry, "server.all.kbytes_in = %.0f\n", stats.server_all_kbytes_in); storeAppendPrintf(sentry, "server.all.kbytes_out = %.0f\n", stats.server_all_kbytes_out); storeAppendPrintf(sentry, "server.http.requests = %.0f\n", stats.server_http_requests); storeAppendPrintf(sentry, "server.http.errors = %.0f\n", stats.server_http_errors); storeAppendPrintf(sentry, "server.http.kbytes_in = %.0f\n", stats.server_http_kbytes_in); storeAppendPrintf(sentry, "server.http.kbytes_out = %.0f\n", stats.server_http_kbytes_out); storeAppendPrintf(sentry, "server.ftp.requests = %.0f\n", stats.server_ftp_requests); storeAppendPrintf(sentry, "server.ftp.errors = %.0f\n", stats.server_ftp_errors); storeAppendPrintf(sentry, "server.ftp.kbytes_in = %.0f\n", stats.server_ftp_kbytes_in); storeAppendPrintf(sentry, "server.ftp.kbytes_out = %.0f\n", stats.server_ftp_kbytes_out); storeAppendPrintf(sentry, "server.other.requests = %.0f\n", stats.server_other_requests); storeAppendPrintf(sentry, "server.other.errors = %.0f\n", stats.server_other_errors); storeAppendPrintf(sentry, "server.other.kbytes_in = %.0f\n", stats.server_other_kbytes_in); storeAppendPrintf(sentry, "server.other.kbytes_out = %.0f\n", stats.server_other_kbytes_out); storeAppendPrintf(sentry, "icp.pkts_sent = %.0f\n", stats.icp_pkts_sent); storeAppendPrintf(sentry, "icp.pkts_recv = %.0f\n", stats.icp_pkts_recv); storeAppendPrintf(sentry, "icp.queries_sent = %.0f\n", stats.icp_queries_sent); storeAppendPrintf(sentry, "icp.replies_sent = %.0f\n", stats.icp_replies_sent); storeAppendPrintf(sentry, "icp.queries_recv = %.0f\n", stats.icp_queries_recv); storeAppendPrintf(sentry, "icp.replies_recv = %.0f\n", stats.icp_replies_recv); storeAppendPrintf(sentry, "icp.query_timeouts = %.0f\n", stats.icp_query_timeouts); storeAppendPrintf(sentry, "icp.replies_queued = %.0f\n", stats.icp_replies_queued); storeAppendPrintf(sentry, "icp.kbytes_sent = %.0f\n", stats.icp_kbytes_sent); storeAppendPrintf(sentry, "icp.kbytes_recv = %.0f\n", stats.icp_kbytes_recv); storeAppendPrintf(sentry, "icp.q_kbytes_sent = %.0f\n", stats.icp_q_kbytes_sent); storeAppendPrintf(sentry, "icp.r_kbytes_sent = %.0f\n", stats.icp_r_kbytes_sent); storeAppendPrintf(sentry, "icp.q_kbytes_recv = %.0f\n", stats.icp_q_kbytes_recv); storeAppendPrintf(sentry, "icp.r_kbytes_recv = %.0f\n", stats.icp_r_kbytes_recv); #if USE_CACHE_DIGESTS storeAppendPrintf(sentry, "icp.times_used = %.0f\n", stats.icp_times_used); storeAppendPrintf(sentry, "cd.times_used = %.0f\n", stats.cd_times_used); storeAppendPrintf(sentry, "cd.msgs_sent = %.0f\n", stats.cd_msgs_sent); storeAppendPrintf(sentry, "cd.msgs_recv = %.0f\n", stats.cd_msgs_recv); storeAppendPrintf(sentry, "cd.memory = %.0f\n", stats.cd_memory); storeAppendPrintf(sentry, "cd.local_memory = %.0f\n", stats.cd_local_memory); storeAppendPrintf(sentry, "cd.kbytes_sent = %.0f\n", stats.cd_kbytes_sent); storeAppendPrintf(sentry, "cd.kbytes_recv = %.0f\n", stats.cd_kbytes_recv); #endif storeAppendPrintf(sentry, "unlink.requests = %.0f\n", stats.unlink_requests); storeAppendPrintf(sentry, "page_faults = %.0f\n", stats.page_faults); storeAppendPrintf(sentry, "select_loops = %.0f\n", stats.select_loops); storeAppendPrintf(sentry, "cpu_time = %f\n", stats.cpu_time); storeAppendPrintf(sentry, "wall_time = %f\n", stats.wall_time); storeAppendPrintf(sentry, "swap.outs = %.0f\n", stats.swap_outs); storeAppendPrintf(sentry, "swap.ins = %.0f\n", stats.swap_ins); storeAppendPrintf(sentry, "swap.files_cleaned = %.0f\n", stats.swap_files_cleaned); storeAppendPrintf(sentry, "aborted_requests = %.0f\n", stats.aborted_requests); storeAppendPrintf(sentry, "hit_validation.attempts = %.0f\n", stats.hitValidationAttempts); storeAppendPrintf(sentry, "hit_validation.refusals.due_to_locking = %.0f\n", stats.hitValidationRefusalsDueToLocking); storeAppendPrintf(sentry, "hit_validation.refusals.due_to_zeroSize = %.0f\n", stats.hitValidationRefusalsDueToZeroSize); storeAppendPrintf(sentry, "hit_validation.refusals.due_to_timeLimit = %.0f\n", stats.hitValidationRefusalsDueToTimeLimit); storeAppendPrintf(sentry, "hit_validation.failures = %.0f\n", stats.hitValidationFailures); } static void statPeerSelect(StoreEntry * sentry) { #if USE_CACHE_DIGESTS StatCounters *f = &statCounter; CachePeer *peer; const int tot_used = f->cd.times_used + f->icp.times_used; /* totals */ static const SBuf label("all peers"); cacheDigestGuessStatsReport(&f->cd.guess, sentry, label); /* per-peer */ storeAppendPrintf(sentry, "\nPer-peer statistics:\n"); for (peer = getFirstPeer(); peer; peer = getNextPeer(peer)) { if (peer->digest) peerDigestStatsReport(peer->digest, sentry); else storeAppendPrintf(sentry, "\nNo peer digest from %s\n", peer->host); storeAppendPrintf(sentry, "\n"); } storeAppendPrintf(sentry, "\nAlgorithm usage:\n"); storeAppendPrintf(sentry, "Cache Digest: %7d (%3d%%)\n", f->cd.times_used, xpercentInt(f->cd.times_used, tot_used)); storeAppendPrintf(sentry, "Icp: %7d (%3d%%)\n", f->icp.times_used, xpercentInt(f->icp.times_used, tot_used)); storeAppendPrintf(sentry, "Total: %7d (%3d%%)\n", tot_used, xpercentInt(tot_used, tot_used)); #else storeAppendPrintf(sentry, "peer digests are disabled; no stats is available.\n"); #endif } static void statDigestBlob(StoreEntry * sentry) { storeAppendPrintf(sentry, "\nCounters:\n"); statCountersDump(sentry); storeAppendPrintf(sentry, "\n5 Min Averages:\n"); statAvgDump(sentry, 5, 0); storeAppendPrintf(sentry, "\nHistograms:\n"); statCountersHistograms(sentry); storeAppendPrintf(sentry, "\nPeer Digests:\n"); statPeerSelect(sentry); storeAppendPrintf(sentry, "\nLocal Digest:\n"); storeDigestReport(sentry); } static double statPctileSvc(double pctile, int interval, int which) { StatCounters *f; StatCounters *l; double x; assert(interval > 0); if (interval > N_COUNT_HIST - 1) interval = N_COUNT_HIST - 1; f = &CountHist[0]; l = &CountHist[interval]; assert(f); assert(l); switch (which) { case PCTILE_HTTP: x = statHistDeltaPctile(l->client_http.allSvcTime,f->client_http.allSvcTime, pctile); break; case PCTILE_HIT: x = statHistDeltaPctile(l->client_http.hitSvcTime,f->client_http.hitSvcTime, pctile); break; case PCTILE_MISS: x = statHistDeltaPctile(l->client_http.missSvcTime,f->client_http.missSvcTime, pctile); break; case PCTILE_NM: x = statHistDeltaPctile(l->client_http.nearMissSvcTime,f->client_http.nearMissSvcTime, pctile); break; case PCTILE_NH: x = statHistDeltaPctile(l->client_http.nearHitSvcTime,f->client_http.nearHitSvcTime, pctile); break; case PCTILE_ICP_QUERY: x = statHistDeltaPctile(l->icp.querySvcTime,f->icp.querySvcTime, pctile); break; case PCTILE_DNS: x = statHistDeltaPctile(l->dns.svcTime,f->dns.svcTime, pctile); break; default: debugs(49, 5, "statPctileSvc: unknown type."); x = 0; } return x; } StatCounters * snmpStatGet(int minutes) { return &CountHist[minutes]; } int stat5minClientRequests(void) { assert(N_COUNT_HIST > 5); return statCounter.client_http.requests - CountHist[5].client_http.requests; } static double statCPUUsage(int minutes) { assert(minutes < N_COUNT_HIST); return Math::doublePercent(CountHist[0].cputime - CountHist[minutes].cputime, tvSubDsec(CountHist[minutes].timestamp, CountHist[0].timestamp)); } double statRequestHitRatio(int minutes) { assert(minutes < N_COUNT_HIST); return Math::doublePercent(CountHist[0].client_http.hits - CountHist[minutes].client_http.hits, CountHist[0].client_http.requests - CountHist[minutes].client_http.requests); } double statRequestHitMemoryRatio(int minutes) { assert(minutes < N_COUNT_HIST); return Math::doublePercent(CountHist[0].client_http.mem_hits - CountHist[minutes].client_http.mem_hits, CountHist[0].client_http.hits - CountHist[minutes].client_http.hits); } double statRequestHitDiskRatio(int minutes) { assert(minutes < N_COUNT_HIST); return Math::doublePercent(CountHist[0].client_http.disk_hits - CountHist[minutes].client_http.disk_hits, CountHist[0].client_http.hits - CountHist[minutes].client_http.hits); } double statByteHitRatio(int minutes) { size_t s; size_t c; #if USE_CACHE_DIGESTS size_t cd; #endif /* size_t might be unsigned */ assert(minutes < N_COUNT_HIST); c = CountHist[0].client_http.kbytes_out.kb - CountHist[minutes].client_http.kbytes_out.kb; s = CountHist[0].server.all.kbytes_in.kb - CountHist[minutes].server.all.kbytes_in.kb; #if USE_CACHE_DIGESTS /* * This ugly hack is here to prevent the user from seeing a * negative byte hit ratio. When we fetch a cache digest from * a neighbor, it gets treated like a cache miss because the * object is consumed internally. Thus, we subtract cache * digest bytes out before calculating the byte hit ratio. */ cd = CountHist[0].cd.kbytes_recv.kb - CountHist[minutes].cd.kbytes_recv.kb; if (s < cd) debugs(18, DBG_IMPORTANT, "STRANGE: srv_kbytes=" << s << ", cd_kbytes=" << cd); s -= cd; #endif if (c > s) return Math::doublePercent(c - s, c); else return (-1.0 * Math::doublePercent(s - c, c)); } static void statClientRequests(StoreEntry * s) { dlink_node *i; ClientHttpRequest *http; StoreEntry *e; char buf[MAX_IPSTRLEN]; for (i = ClientActiveRequests.head; i; i = i->next) { const char *p = nullptr; http = static_cast(i->data); assert(http); ConnStateData * conn = http->getConn(); storeAppendPrintf(s, "Connection: %p\n", conn); if (conn != nullptr) { const int fd = conn->clientConnection->fd; storeAppendPrintf(s, "\tFD %d, read %" PRId64 ", wrote %" PRId64 "\n", fd, fd_table[fd].bytes_read, fd_table[fd].bytes_written); storeAppendPrintf(s, "\tFD desc: %s\n", fd_table[fd].desc); storeAppendPrintf(s, "\tin: buf %p, used %ld, free %ld\n", conn->inBuf.rawContent(), (long int) conn->inBuf.length(), (long int) conn->inBuf.spaceSize()); storeAppendPrintf(s, "\tremote: %s\n", conn->clientConnection->remote.toUrl(buf,MAX_IPSTRLEN)); storeAppendPrintf(s, "\tlocal: %s\n", conn->clientConnection->local.toUrl(buf,MAX_IPSTRLEN)); storeAppendPrintf(s, "\tnrequests: %u\n", conn->pipeline.nrequests); } storeAppendPrintf(s, "uri %s\n", http->uri); storeAppendPrintf(s, "logType %s\n", http->loggingTags().c_str()); storeAppendPrintf(s, "out.offset %ld, out.size %lu\n", (long int) http->out.offset, (unsigned long int) http->out.size); storeAppendPrintf(s, "req_sz %ld\n", (long int) http->req_sz); e = http->storeEntry(); storeAppendPrintf(s, "entry %p/%s\n", e, e ? e->getMD5Text() : "N/A"); storeAppendPrintf(s, "start %ld.%06d (%f seconds ago)\n", (long int) http->al->cache.start_time.tv_sec, (int) http->al->cache.start_time.tv_usec, tvSubDsec(http->al->cache.start_time, current_time)); #if USE_AUTH if (http->request->auth_user_request != nullptr) p = http->request->auth_user_request->username(); else #endif if (http->request->extacl_user.size() > 0) { p = http->request->extacl_user.termedBuf(); } if (!p && conn != nullptr && conn->clientConnection->rfc931[0]) p = conn->clientConnection->rfc931; #if USE_OPENSSL if (!p && conn != nullptr && Comm::IsConnOpen(conn->clientConnection)) p = sslGetUserEmail(fd_table[conn->clientConnection->fd].ssl.get()); #endif if (!p) p = dash_str; storeAppendPrintf(s, "username %s\n", p); #if USE_DELAY_POOLS storeAppendPrintf(s, "delay_pool %d\n", DelayId::DelayClient(http).pool()); #endif storeAppendPrintf(s, "\n"); } } #if STAT_GRAPHS /* * urgh, i don't like these, but they do cut the amount of code down immensely */ #define GRAPH_PER_MIN(Y) \ for (i=0;i<(N_COUNT_HIST-2);++i) { \ dt = tvSubDsec(CountHist[i+1].timestamp, CountHist[i].timestamp); \ if (dt <= 0.0) \ break; \ storeAppendPrintf(e, "%lu,%0.2f:", \ CountHist[i].timestamp.tv_sec, \ ((CountHist[i].Y - CountHist[i+1].Y) / dt)); \ } #define GRAPH_PER_HOUR(Y) \ for (i=0;i<(N_COUNT_HOUR_HIST-2);++i) { \ dt = tvSubDsec(CountHourHist[i+1].timestamp, CountHourHist[i].timestamp); \ if (dt <= 0.0) \ break; \ storeAppendPrintf(e, "%lu,%0.2f:", \ CountHourHist[i].timestamp.tv_sec, \ ((CountHourHist[i].Y - CountHourHist[i+1].Y) / dt)); \ } #define GRAPH_TITLE(X,Y) storeAppendPrintf(e,"%s\t%s\t",X,Y); #define GRAPH_END storeAppendPrintf(e,"\n"); #define GENGRAPH(X,Y,Z) \ GRAPH_TITLE(Y,Z) \ GRAPH_PER_MIN(X) \ GRAPH_PER_HOUR(X) \ GRAPH_END static void statGraphDump(StoreEntry * e) { int i; double dt; GENGRAPH(client_http.requests, "client_http.requests", "Client HTTP requests/sec"); GENGRAPH(client_http.hits, "client_http.hits", "Client HTTP hits/sec"); GENGRAPH(client_http.errors, "client_http.errors", "Client HTTP errors/sec"); GENGRAPH(client_http.kbytes_in.kb, "client_http.kbytes_in", "Client HTTP kbytes_in/sec"); GENGRAPH(client_http.kbytes_out.kb, "client_http.kbytes_out", "Client HTTP kbytes_out/sec"); // TODO: http median service times GENGRAPH(server.all.requests, "server.all.requests", "Server requests/sec"); GENGRAPH(server.all.errors, "server.all.errors", "Server errors/sec"); GENGRAPH(server.all.kbytes_in.kb, "server.all.kbytes_in", "Server total kbytes_in/sec"); GENGRAPH(server.all.kbytes_out.kb, "server.all.kbytes_out", "Server total kbytes_out/sec"); GENGRAPH(server.http.requests, "server.http.requests", "Server HTTP requests/sec"); GENGRAPH(server.http.errors, "server.http.errors", "Server HTTP errors/sec"); GENGRAPH(server.http.kbytes_in.kb, "server.http.kbytes_in", "Server HTTP kbytes_in/sec"); GENGRAPH(server.http.kbytes_out.kb, "server.http.kbytes_out", "Server HTTP kbytes_out/sec"); GENGRAPH(server.ftp.requests, "server.ftp.requests", "Server FTP requests/sec"); GENGRAPH(server.ftp.errors, "server.ftp.errors", "Server FTP errors/sec"); GENGRAPH(server.ftp.kbytes_in.kb, "server.ftp.kbytes_in", "Server FTP kbytes_in/sec"); GENGRAPH(server.ftp.kbytes_out.kb, "server.ftp.kbytes_out", "Server FTP kbytes_out/sec"); GENGRAPH(server.other.requests, "server.other.requests", "Server other requests/sec"); GENGRAPH(server.other.errors, "server.other.errors", "Server other errors/sec"); GENGRAPH(server.other.kbytes_in.kb, "server.other.kbytes_in", "Server other kbytes_in/sec"); GENGRAPH(server.other.kbytes_out.kb, "server.other.kbytes_out", "Server other kbytes_out/sec"); GENGRAPH(icp.pkts_sent, "icp.pkts_sent", "ICP packets sent/sec"); GENGRAPH(icp.pkts_recv, "icp.pkts_recv", "ICP packets received/sec"); GENGRAPH(icp.kbytes_sent.kb, "icp.kbytes_sent", "ICP kbytes_sent/sec"); GENGRAPH(icp.kbytes_recv.kb, "icp.kbytes_recv", "ICP kbytes_received/sec"); // TODO: icp median service times // TODO: dns median service times GENGRAPH(unlink.requests, "unlink.requests", "Cache File unlink requests/sec"); GENGRAPH(page_faults, "page_faults", "System Page Faults/sec"); GENGRAPH(select_loops, "select_loops", "System Select Loop calls/sec"); GENGRAPH(cputime, "cputime", "CPU utilisation"); } #endif /* STAT_GRAPHS */