|
44 | 44 | #include <assert.h> |
45 | 45 | #include <fcntl.h> |
46 | 46 | #include <limits.h> |
| 47 | +#include <math.h> |
47 | 48 |
|
48 | 49 | #include "hiredis.h" |
49 | 50 | #include "sds.h" |
@@ -74,6 +75,7 @@ static struct config { |
74 | 75 | int monitor_mode; |
75 | 76 | int pubsub_mode; |
76 | 77 | int latency_mode; |
| 78 | + int latency_dist_mode; |
77 | 79 | int latency_history; |
78 | 80 | int cluster_mode; |
79 | 81 | int cluster_reissue_command; |
@@ -741,6 +743,8 @@ static int parseOptions(int argc, char **argv) { |
741 | 743 | config.output = OUTPUT_CSV; |
742 | 744 | } else if (!strcmp(argv[i],"--latency")) { |
743 | 745 | config.latency_mode = 1; |
| 746 | + } else if (!strcmp(argv[i],"--latency-dist")) { |
| 747 | + config.latency_dist_mode = 1; |
744 | 748 | } else if (!strcmp(argv[i],"--latency-history")) { |
745 | 749 | config.latency_mode = 1; |
746 | 750 | config.latency_history = 1; |
@@ -833,6 +837,8 @@ static void usage(void) { |
833 | 837 | " --latency Enter a special mode continuously sampling latency.\n" |
834 | 838 | " --latency-history Like --latency but tracking latency changes over time.\n" |
835 | 839 | " Default time interval is 15 sec. Change it using -i.\n" |
| 840 | +" --latency-dist Shows latency as a spectrum, requires xterm 256 colors.\n" |
| 841 | +" Default time interval is 1 sec. Change it using -i.\n" |
836 | 842 | " --slave Simulate a slave showing commands received from the master.\n" |
837 | 843 | " --rdb <filename> Transfer an RDB dump from remote server to local file.\n" |
838 | 844 | " --pipe Transfer raw Redis protocol from stdin to server.\n" |
@@ -1071,6 +1077,146 @@ static void latencyMode(void) { |
1071 | 1077 | } |
1072 | 1078 | } |
1073 | 1079 |
|
| 1080 | +/*------------------------------------------------------------------------------ |
| 1081 | + * Latency distribution mode -- requires 256 colors xterm |
| 1082 | + *--------------------------------------------------------------------------- */ |
| 1083 | + |
| 1084 | +#define LATENCY_DIST_DEFAULT_INTERVAL 1000 /* milliseconds. */ |
| 1085 | +#define LATENCY_DIST_MIN_GRAY 233 /* Less than that is too hard to see gray. */ |
| 1086 | +#define LATENCY_DIST_MAX_GRAY 255 |
| 1087 | +#define LATENCY_DIST_GRAYS (LATENCY_DIST_MAX_GRAY-LATENCY_DIST_MIN_GRAY+1) |
| 1088 | + |
| 1089 | +/* Structure to store samples distribution. */ |
| 1090 | +struct distsamples { |
| 1091 | + long long max; /* Max latency to fit into this interval (usec). */ |
| 1092 | + long long count; /* Number of samples in this interval. */ |
| 1093 | + int character; /* Associated character in visualization. */ |
| 1094 | +}; |
| 1095 | + |
| 1096 | +/* Helper function for latencyDistMode(). Performs the spectrum visualization |
| 1097 | + * of the collected samples targeting an xterm 256 terminal. |
| 1098 | + * |
| 1099 | + * Takes an array of distsamples structures, ordered from smaller to bigger |
| 1100 | + * 'max' value. Last sample max must be 0, to mean that it olds all the |
| 1101 | + * samples greater than the previous one, and is also the stop sentinel. |
| 1102 | + * |
| 1103 | + * "tot' is the total number of samples in the different buckets, so it |
| 1104 | + * is the SUM(samples[i].conut) for i to 0 up to the max sample. |
| 1105 | + * |
| 1106 | + * As a side effect the function sets all the buckets count to 0. */ |
| 1107 | +void showLatencyDistSamples(struct distsamples *samples, long long tot) { |
| 1108 | + int j; |
| 1109 | + |
| 1110 | + /* We convert samples into a number between 0 and DIST_GRAYS, |
| 1111 | + * proportional to the percentage a given bucket represents. |
| 1112 | + * This way intensity of the different parts of the spectrum |
| 1113 | + * don't change relative to the number of requests, which avoids to |
| 1114 | + * pollute the visualization with non-latency related info. */ |
| 1115 | + printf("\033[38;5;0m"); /* Set foreground color to black. */ |
| 1116 | + for (j = 0; ; j++) { |
| 1117 | + float color = (float) samples[j].count / tot * LATENCY_DIST_GRAYS; |
| 1118 | + color = ceil(color) + (LATENCY_DIST_MIN_GRAY-1); |
| 1119 | + if (color == LATENCY_DIST_MIN_GRAY-1) { |
| 1120 | + printf("\033[48;5;0m "); |
| 1121 | + } else { |
| 1122 | + printf("\033[48;5;%dm%c", (int)color, samples[j].character); |
| 1123 | + } |
| 1124 | + samples[j].count = 0; |
| 1125 | + if (samples[j].max == 0) break; /* Last sample. */ |
| 1126 | + } |
| 1127 | + printf("\033[0m\n"); |
| 1128 | + fflush(stdout); |
| 1129 | +} |
| 1130 | + |
| 1131 | +/* Show the legend: different buckets values and colors meaning, so |
| 1132 | + * that the spectrum is more easily readable. */ |
| 1133 | +void showLatencyDistLegend(void) { |
| 1134 | + printf(". - * 0.01 0.125 0.5 milliseconds\n"); |
| 1135 | + printf("1,2,3,...,9 from 1 to 9 milliseconds\n"); |
| 1136 | + printf("A,B,C,D,E 10,20,30,40,50 milliseconds\n"); |
| 1137 | + printf("F,G,H,I,J .1,.2,.3,.4,.5 seconds\n"); |
| 1138 | + printf("K,L,M,N,O,P,Q,? 1,2,4,8,16,30,60,>60 seconds\n"); |
| 1139 | + printf("---------------------------------------------\n"); |
| 1140 | +} |
| 1141 | + |
| 1142 | +static void latencyDistMode(void) { |
| 1143 | + redisReply *reply; |
| 1144 | + long long start, latency, count = 0; |
| 1145 | + long long history_interval = |
| 1146 | + config.interval ? config.interval/1000 : |
| 1147 | + LATENCY_DIST_DEFAULT_INTERVAL; |
| 1148 | + long long history_start = ustime(); |
| 1149 | + int j, outputs = 0; |
| 1150 | + |
| 1151 | + struct distsamples samples[] = { |
| 1152 | + /* We use a mostly logarithmic scale, with certain linear intervals |
| 1153 | + * which are more interesting than others, like 1-10 milliseconds |
| 1154 | + * range. */ |
| 1155 | + {10,0,'.'}, /* 0.01 ms */ |
| 1156 | + {125,0,'-'}, /* 0.125 ms */ |
| 1157 | + {250,0,'*'}, /* 0.25 ms */ |
| 1158 | + {500,0,'#'}, /* 0.5 ms */ |
| 1159 | + {1000,0,'1'}, /* 1 ms */ |
| 1160 | + {2000,0,'2'}, /* 2 ms */ |
| 1161 | + {3000,0,'3'}, /* 3 ms */ |
| 1162 | + {4000,0,'4'}, /* 4 ms */ |
| 1163 | + {5000,0,'5'}, /* 5 ms */ |
| 1164 | + {6000,0,'6'}, /* 6 ms */ |
| 1165 | + {7000,0,'7'}, /* 7 ms */ |
| 1166 | + {8000,0,'8'}, /* 8 ms */ |
| 1167 | + {9000,0,'9'}, /* 9 ms */ |
| 1168 | + {10000,0,'A'}, /* 10 ms */ |
| 1169 | + {20000,0,'B'}, /* 20 ms */ |
| 1170 | + {30000,0,'C'}, /* 30 ms */ |
| 1171 | + {40000,0,'D'}, /* 40 ms */ |
| 1172 | + {50000,0,'E'}, /* 50 ms */ |
| 1173 | + {100000,0,'F'}, /* 0.1 s */ |
| 1174 | + {200000,0,'G'}, /* 0.2 s */ |
| 1175 | + {300000,0,'H'}, /* 0.3 s */ |
| 1176 | + {400000,0,'I'}, /* 0.4 s */ |
| 1177 | + {500000,0,'J'}, /* 0.5 s */ |
| 1178 | + {1000000,0,'K'}, /* 1 s */ |
| 1179 | + {2000000,0,'L'}, /* 2 s */ |
| 1180 | + {4000000,0,'M'}, /* 4 s */ |
| 1181 | + {8000000,0,'N'}, /* 8 s */ |
| 1182 | + {16000000,0,'O'}, /* 16 s */ |
| 1183 | + {30000000,0,'P'}, /* 30 s */ |
| 1184 | + {60000000,0,'Q'}, /* 1 minute */ |
| 1185 | + {0,0,'?'}, /* > 1 minute */ |
| 1186 | + }; |
| 1187 | + |
| 1188 | + if (!context) exit(1); |
| 1189 | + while(1) { |
| 1190 | + start = ustime(); |
| 1191 | + reply = redisCommand(context,"PING"); |
| 1192 | + if (reply == NULL) { |
| 1193 | + fprintf(stderr,"\nI/O error\n"); |
| 1194 | + exit(1); |
| 1195 | + } |
| 1196 | + latency = ustime()-start; |
| 1197 | + freeReplyObject(reply); |
| 1198 | + count++; |
| 1199 | + |
| 1200 | + /* Populate the relevant bucket. */ |
| 1201 | + for (j = 0; ; j++) { |
| 1202 | + if (samples[j].max == 0 || latency <= samples[j].max) { |
| 1203 | + samples[j].count++; |
| 1204 | + break; |
| 1205 | + } |
| 1206 | + } |
| 1207 | + |
| 1208 | + /* From time to time show the spectrum. */ |
| 1209 | + if (count && (ustime()-history_start)/1000 > history_interval) { |
| 1210 | + if ((outputs++ % 20) == 0) |
| 1211 | + showLatencyDistLegend(); |
| 1212 | + showLatencyDistSamples(samples,count); |
| 1213 | + history_start = ustime(); |
| 1214 | + count = 0; |
| 1215 | + } |
| 1216 | + usleep(LATENCY_SAMPLE_RATE * 1000); |
| 1217 | + } |
| 1218 | +} |
| 1219 | + |
1074 | 1220 | /*------------------------------------------------------------------------------ |
1075 | 1221 | * Slave mode |
1076 | 1222 | *--------------------------------------------------------------------------- */ |
@@ -1887,6 +2033,7 @@ int main(int argc, char **argv) { |
1887 | 2033 | config.monitor_mode = 0; |
1888 | 2034 | config.pubsub_mode = 0; |
1889 | 2035 | config.latency_mode = 0; |
| 2036 | + config.latency_dist_mode = 0; |
1890 | 2037 | config.latency_history = 0; |
1891 | 2038 | config.cluster_mode = 0; |
1892 | 2039 | config.slave_mode = 0; |
@@ -1921,6 +2068,12 @@ int main(int argc, char **argv) { |
1921 | 2068 | latencyMode(); |
1922 | 2069 | } |
1923 | 2070 |
|
| 2071 | + /* Latency distribution mode */ |
| 2072 | + if (config.latency_dist_mode) { |
| 2073 | + if (cliConnect(0) == REDIS_ERR) exit(1); |
| 2074 | + latencyDistMode(); |
| 2075 | + } |
| 2076 | + |
1924 | 2077 | /* Slave mode */ |
1925 | 2078 | if (config.slave_mode) { |
1926 | 2079 | if (cliConnect(0) == REDIS_ERR) exit(1); |
|
0 commit comments