Line data Source code
1 : #include <stdio.h>
2 : #include <stdlib.h>
3 : #include <sys/stat.h>
4 : #include <sys/types.h>
5 : #include <fcntl.h>
6 : #include <unistd.h>
7 : #include <errno.h>
8 : #include <string.h>
9 : #include <signal.h>
10 :
11 : #include "statistics.h"
12 : #include "log.h"
13 : #include "fifo.h"
14 : #include "felix/felix_fid.h"
15 : #include "netio/netio.h"
16 : #include "flx_api.h"
17 : #include "app.h"
18 :
19 : #include "jWrite.h"
20 :
21 : const char* requested_regs[5] = {"TTC_L1ID_MONITOR", "TTC_ECR_MONITOR_VALUE", "TTC_TTYPE_MONITOR_VALUE", "TTC_BCR_PERIODICITY_MONITOR_VALUE", "TTC_BCR_COUNTER_VALUE"};
22 : const char* is_vars[5] = {"xl1id", "ecrs_from_ttc", "ttype_from_ttc", "bcr_mismatch", "bcr"};
23 : int available_regs[5] = {0,0,0,0,0};
24 : uint64_t current_bcr_mismatch = 0;
25 : int bcr_periodicity_available = 0;
26 :
27 46 : void statistics_init(struct felix_statistics* stats, char* fifoname, netio_tag_t fid, struct flx* flx, struct config* cfg, struct cmem_buffer* buf)
28 : {
29 46 : memset(stats->buf, 0, STATISTICS_MAX_SIZE);
30 :
31 46 : stats->flx = flx;
32 46 : stats->cfg = cfg;
33 46 : stats->buffer = buf;
34 :
35 46 : if (fifoname){
36 2 : stats->fifoname = strdup(fifoname);
37 2 : stats->fid = fid;
38 2 : stats->fd = open_fifo(fifoname);
39 2 : int rc;
40 2 : int pipe_sz = fcntl(stats->fd, F_SETPIPE_SZ, STATISTICS_MAX_SIZE);
41 2 : LOG_INFO("Maximum stats buffer size = %d", pipe_sz);
42 2 : const char sep[2] = ".";
43 2 : rc = gethostname(stats->hostname_long, 256);
44 2 : if (rc < 0) {memset(stats->hostname_long, '\0', 256);}
45 2 : else {stats->hostname = strtok(stats->hostname_long, sep);}
46 : }
47 46 : }
48 :
49 :
50 44 : void statistics_reg_init(struct felix_statistics* stats){
51 264 : for(uint i = 0; i < 5; i++){
52 220 : if (flx_cfg_check_option(stats->flx, requested_regs[i])){
53 0 : available_regs[i] = 1;
54 : }
55 : }
56 44 : if(stats->cfg->warn_bcr_period && flx_cfg_check_option(stats->flx, "TTC_BCR_PERIODICITY_MONITOR_VALUE")){
57 0 : bcr_periodicity_available = 1;
58 : }
59 44 : }
60 :
61 :
62 44 : void statistics_mode_init(struct felix_statistics* stats){
63 44 : stats->mode = flx_get_mode(stats->flx);
64 44 : }
65 :
66 :
67 0 : double get_dma_occupancy(struct felix_statistics* stats){
68 0 : uint64_t rd_ptr = (uint64_t)flx_get_read_ptr(stats->flx, stats->buffer);
69 0 : uint64_t wr_ptr = (uint64_t)flx_get_write_ptr(stats->flx, stats->buffer);
70 0 : int even_dma_bits = flx_dma_cmp_even_bits(stats->flx);
71 0 : uint64_t size = stats->buffer->size;
72 0 : double available;
73 0 : if (wr_ptr == rd_ptr){
74 0 : available = even_dma_bits ? stats->buffer->size : 0;
75 0 : } else if (wr_ptr > rd_ptr){
76 0 : available = even_dma_bits ? (size - (wr_ptr - rd_ptr)) : -1073741824.;
77 : } else {
78 0 : available = !even_dma_bits ? (rd_ptr - wr_ptr) : -1073741824.;
79 : }
80 0 : return (available/1024./1024.);
81 : }
82 :
83 :
84 515 : void statistics_write_tohost(struct felix_statistics* stats, struct tohost_stats_data* data, struct elinktable* elinktable)
85 : {
86 : // Calculate the duration
87 515 : struct timespec current_ts;
88 515 : clock_gettime(CLOCK_MONOTONIC_RAW, ¤t_ts);
89 515 : double seconds = current_ts.tv_sec - data->last_ts.tv_sec
90 515 : + 1e-9*(current_ts.tv_nsec - data->last_ts.tv_nsec);
91 :
92 : //if no fifo just compute the rates and reset counters
93 515 : if (!stats->fifoname){
94 1312 : for(unsigned i=0; i<elinktable->num_elinks; i++) {
95 797 : local_elink_t elink = elinktable->elinks[i];
96 797 : struct elink_entry* entry = &elinktable->entries[elink];
97 797 : entry->received_chunk_rate =
98 797 : (entry->counters.received_chunks - entry->last_counters.received_chunks)/1000./seconds;
99 797 : entry->last_counters.received_chunks = entry->counters.received_chunks;
100 : }
101 515 : data->last_ts = current_ts;
102 515 : return;
103 : }
104 :
105 0 : char ts[72];
106 0 : struct timespec now;
107 0 : clock_gettime(CLOCK_REALTIME, &now);
108 0 : struct tm *tm = localtime(&now.tv_sec);
109 : // sprintf(ts, "%02d/%02d/%04d %02d:%02d:%02d", tm->tm_mday, tm->tm_mon + 1, tm->tm_year+1900, tm->tm_hour, tm->tm_min, tm->tm_sec);
110 0 : strftime(ts, 72, "%d/%m/%Y %H:%M:%S", tm);
111 :
112 0 : char sfid[30];
113 0 : char timestamp[30];
114 0 : char device[30];
115 0 : char gfid[30];
116 :
117 : //BUSY status
118 0 : uint64_t busy = flx_cfg_get_option(stats->flx, "TTC_DEC_CTRL_BUSY_OUTPUT_STATUS");
119 :
120 : //ERS message for non-periodic BCR
121 0 : if(bcr_periodicity_available){
122 0 : current_bcr_mismatch = flx_cfg_get_option(stats->flx, "TTC_BCR_PERIODICITY_MONITOR_VALUE");
123 : }
124 0 : struct jWriteControl jwc;
125 0 : snprintf(timestamp, 30, "%lu.%09lu", now.tv_sec, now.tv_nsec);
126 0 : snprintf(device, 30, "%d", stats->cfg->device);
127 0 : snprintf(gfid, 30, "%lu", stats->fid);
128 : // GENERAL STATISTICS /////////////
129 : // STATISTICS_MAX_SIZE-1 to guarantee null-terminmation (jWrite does not account for it)
130 0 : jwOpen(&jwc, stats->buf, STATISTICS_MAX_SIZE-1, JW_OBJECT, JW_COMPACT);
131 0 : stats->buf[STATISTICS_MAX_SIZE-1] = 0; //clean possible newline from previous round
132 0 : jwObj_ulong(&jwc, "fid", stats->fid);
133 0 : jwObj_string(&jwc, "hostname", stats->hostname);
134 0 : jwObj_string(&jwc, "device", device);
135 0 : jwObj_string(&jwc, "ts", ts);
136 0 : jwObj_string(&jwc, "timestamp", timestamp);
137 0 : jwObj_string(&jwc, "app", "tohost");
138 :
139 : // GLOBAL
140 0 : jwObj_object(&jwc, "global");
141 0 : jwObj_double(&jwc, "throughput[Gbps]",
142 0 : (data->counters.bytes_processed - data->last_counters.bytes_processed)*8/1000./1000./1000./seconds); //Gb/s
143 0 : data->last_counters.bytes_processed = data->counters.bytes_processed;
144 0 : jwObj_double(&jwc, "blockrate[kHz]",
145 0 : (data->counters.blocks_processed - data->last_counters.blocks_processed)/1000./seconds); //kBlocks/s
146 0 : data->last_counters.blocks_processed = data->counters.blocks_processed;
147 0 : jwObj_double(&jwc, "chunkrate[kHz]",
148 0 : (data->counters.chunks_processed - data->last_counters.chunks_processed)/1000./seconds); //kChunks/s
149 0 : data->last_counters.chunks_processed = data->counters.chunks_processed;
150 0 : jwObj_ulong(&jwc, "interrupts", (data->counters.interrupts - data->last_counters.interrupts));
151 0 : data->last_counters.interrupts = data->counters.interrupts;
152 : //netio completion objects
153 0 : jwObj_ulong(&jwc, "daq_co", app.fm_socket.completion_stack.available_objects);
154 0 : jwObj_ulong(&jwc, "dcs_co", app.dcs_usocket.completion_stack.available_objects);
155 0 : jwObj_ulong(&jwc, "ttc_co", 0);
156 : //netio pages
157 0 : jwObj_ulong(&jwc, "daq_pages", app.daq_min_pages);
158 0 : jwObj_ulong(&jwc, "dcs_pages", app.dcs_min_pages);
159 0 : jwObj_ulong(&jwc, "ttc_pages", app.ttc_min_pages);
160 0 : jwObj_ulong(&jwc, "daq_subs", stats->daq_sub_table->num_subscriptions);
161 0 : jwObj_ulong(&jwc, "dcs_subs", (stats->dcs_sub_table == NULL) ? 0 : stats->dcs_sub_table->num_subscriptions);
162 0 : jwObj_ulong(&jwc, "buffer_events",
163 0 : (data->counters.buffer_available_events - data->last_counters.buffer_available_events));
164 0 : data->last_counters.buffer_available_events = data->counters.buffer_available_events;
165 0 : jwObj_ulong(&jwc, "polls", (data->counters.polls - data->last_counters.polls));
166 0 : data->last_counters.polls = data->counters.polls;
167 0 : jwObj_ulong(&jwc, "bcr_periodicity", current_bcr_mismatch);
168 0 : jwObj_double(&jwc, "available_buf", get_dma_occupancy(stats));
169 0 : jwObj_ulong(&jwc, "busy", busy);
170 :
171 0 : for (int i =0; i < 5; i++){
172 0 : if(available_regs[i]){
173 0 : jwObj_ulong(&jwc, is_vars[i], flx_cfg_get_option(stats->flx, requested_regs[i]));
174 : } else {
175 0 : jwObj_ulong(&jwc, is_vars[i], 0);
176 : }
177 : }
178 0 : jwEnd(&jwc);
179 :
180 : //PER-ELINK
181 0 : jwObj_object(&jwc, "stats");
182 0 : jwObj_array(&jwc, "elink");
183 0 : jwArr_string(&jwc, "chunks");
184 0 : jwArr_string(&jwc, "rate"); //kHz
185 0 : jwArr_string(&jwc, "dropped");
186 0 : jwArr_string(&jwc, "largest_size");
187 0 : jwArr_string(&jwc, "avg_size");
188 0 : jwArr_string(&jwc, "fw_error");
189 0 : jwArr_string(&jwc, "sw_error");
190 0 : jwArr_string(&jwc, "fw_trunc");
191 0 : jwArr_string(&jwc, "sw_trunc");
192 0 : jwArr_string(&jwc, "fw_crc");
193 0 : jwEnd(&jwc);
194 :
195 0 : for(unsigned i=0; i<elinktable->num_elinks; i++) {
196 0 : local_elink_t elink = elinktable->elinks[i];
197 0 : struct elink_entry* entry = &elinktable->entries[elink];
198 : // NOTE: stats->fid is the STATS elink. The actual elink is from the elink table. It may have streams
199 : // but these are not counted individually so we set streams to 0. Other the info is from elink_entry.
200 0 : uint64_t fid = get_fid_from_ids(stats->cfg->did, stats->cfg->cid, elinktable->elinks[i], 0, get_vid(stats->fid), is_to_flx(stats->fid), entry->is_virtual);
201 0 : entry->received_chunk_rate =
202 0 : (entry->counters.received_chunks - entry->last_counters.received_chunks)/1000./seconds;
203 :
204 0 : snprintf(sfid, 30, "0x%lx", fid);
205 0 : jwObj_array(&jwc, sfid);
206 0 : uint64_t chunks = (entry->counters.processed_chunks - entry->last_counters.processed_chunks);
207 0 : uint64_t size = (entry->counters.total_chunksize - entry->last_counters.total_chunksize);
208 0 : entry->last_counters.total_chunksize = entry->counters.total_chunksize;
209 0 : entry->last_counters.processed_chunks = entry->counters.processed_chunks;
210 0 : jwArr_ulong(&jwc, entry->counters.processed_chunks);
211 0 : jwArr_double(&jwc, chunks/1000./seconds); //kHz
212 0 : jwArr_ulong(&jwc, entry->counters.dropped);
213 0 : jwArr_ulong(&jwc, entry->counters.largest_chunksize);
214 0 : entry->counters.largest_chunksize = 0;
215 :
216 0 : jwArr_double(&jwc, chunks ? size/chunks : 0);
217 0 : jwArr_ulong(&jwc, entry->decoder.counters.fw_error_chunks);
218 0 : jwArr_ulong(&jwc, entry->decoder.counters.sw_error_chunks);
219 0 : jwArr_ulong(&jwc, entry->decoder.counters.fw_trunc_chunks);
220 0 : jwArr_ulong(&jwc, entry->decoder.counters.sw_trunc_chunks);
221 0 : if(stats->mode == FLX_MODE_FULL){
222 0 : jwArr_ulong(&jwc, entry->decoder.counters.fw_crc_chunks);
223 : } else{
224 0 : jwArr_ulong(&jwc, 0);
225 : }
226 0 : jwEnd(&jwc);
227 : }
228 :
229 0 : jwEnd(&jwc);
230 0 : int ret = jwClose(&jwc);
231 0 : if (ret == 0){
232 0 : int count = strlen(stats->buf);
233 : //replace null-termination with newline
234 0 : if (count >= STATISTICS_MAX_SIZE){
235 0 : LOG_ERR("Statistics output error. Trying to write newline at %d out of range.", count);
236 0 : return;
237 : }
238 0 : stats->buf[count] = '\n';
239 : //account for newline characted
240 0 : ++count;
241 0 : write(stats->fd, stats->buf, count);
242 : } else {
243 0 : LOG_ERR("Statistics output error: %s.", jwErrorToString(ret));
244 : }
245 :
246 0 : data->last_ts = current_ts;
247 : }
248 :
249 :
250 8 : void statistics_write_toflx(struct felix_statistics* stats, struct toflx_stats_data* data, struct timespec* current_ts)
251 : {
252 : // Calculate the duration
253 8 : double const seconds = current_ts->tv_sec - data->last_ts.tv_sec + 1e-9 * (current_ts->tv_nsec - data->last_ts.tv_nsec);
254 :
255 : // Get current time
256 8 : struct timespec now;
257 8 : clock_gettime(CLOCK_REALTIME, &now);
258 :
259 8 : char ts[72];
260 8 : struct tm *tm = localtime(&now.tv_sec);
261 8 : strftime(ts, 72, "%d/%m/%Y %H:%M:%S", tm);
262 :
263 8 : char timestamp[30];
264 8 : char device[30];
265 :
266 8 : struct jWriteControl jwc;
267 8 : snprintf(timestamp, 30, "%lu.%09lu", now.tv_sec, now.tv_nsec);
268 8 : snprintf(device, 30, "%d", stats->cfg->device);
269 :
270 : // STATISTICS_MAX_SIZE-1 to guarantee null-terminmation (jWrite does not account for it)
271 8 : jwOpen(&jwc, stats->buf, STATISTICS_MAX_SIZE-1, JW_OBJECT, JW_COMPACT);
272 8 : stats->buf[STATISTICS_MAX_SIZE-1] = 0; //clean possible newline from previous round
273 8 : jwObj_ulong(&jwc, "fid", stats->fid);
274 8 : jwObj_string(&jwc, "hostname", stats->hostname);
275 8 : jwObj_string(&jwc, "device", device);
276 8 : jwObj_string(&jwc, "app", "toflx");
277 8 : jwObj_string(&jwc, "ts", ts);
278 8 : jwObj_string(&jwc, "timestamp", timestamp);
279 8 : jwObj_double(&jwc, "throughput[Gbps]", data->bytes_received*8./1000./1000./1000./seconds);
280 8 : jwObj_double(&jwc, "msg_rate[kHz]", data->messages_received/1000./seconds);
281 8 : jwObj_double(&jwc, "dma_rate[kHz]", data->dma_transfers/1000./seconds);
282 8 : jwObj_ulong(&jwc, "msgs_received", data->toflx_received);
283 8 : int ret = jwClose(&jwc);
284 8 : if (ret == 0){
285 8 : int count = strlen(stats->buf);
286 : //replace null-termination with newline
287 8 : if (count >= STATISTICS_MAX_SIZE){
288 0 : LOG_ERR("Statistics output error. Trying to write newline at %d out of range.", count);
289 0 : return;
290 : }
291 8 : stats->buf[count] = '\n';
292 : //account for newline characted
293 8 : ++count;
294 8 : write(stats->fd, stats->buf, count);
295 : } else {
296 0 : LOG_ERR("Statistics output error: %s.", jwErrorToString(ret));
297 : }
298 : }
299 :
300 :
301 10 : void reset_toflx_stats_data(struct toflx_stats_data* data, struct timespec* current_ts)
302 : {
303 10 : data->bytes_received = 0;
304 10 : data->messages_received = 0;
305 10 : data->dma_transfers = 0;
306 10 : data->toflx_received = 0;
307 10 : if (current_ts) {
308 10 : data->last_ts.tv_sec = current_ts->tv_sec;
309 10 : data->last_ts.tv_nsec = current_ts->tv_nsec;
310 : } else {
311 0 : data->last_ts.tv_sec = 0;
312 0 : data->last_ts.tv_nsec = 0;
313 : }
314 10 : }
315 :
316 :
317 46 : void statistics_free(struct felix_statistics* stats)
318 : {
319 46 : if (stats->fifoname) free(stats->fifoname);
320 46 : }
|