1 /*
2 * CDDL HEADER START
3 *
4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License (the "License").
6 * You may not use this file except in compliance with the License.
7 *
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 * or http://www.opensolaris.org/os/licensing.
10 * See the License for the specific language governing permissions
11 * and limitations under the License.
12 *
13 * When distributing Covered Code, include this CDDL HEADER in each
14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 * If applicable, add the following below this CDDL HEADER, with the
16 * fields enclosed by brackets "[]" replaced with your own identifying
17 * information: Portions Copyright [yyyy] [name of copyright owner]
18 *
19 * CDDL HEADER END
20 */
21
22 /*
23 * Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved.
24 */
25
26 /*
27 * smbstat: Server Message Block File System statistics
28 *
29 * The statistics this CLI displays come from two sources:
30 *
31 * 1) The kernel module 'smbsrv'.
32 * 2) The SMB workers task queue statistics the task queue manager of Solaris
33 * maintains.
34 *
35 * The flow of the code is the following:
36 *
37 *
38 * +----------------+
39 * | Initialization |
40 * +----------------+
41 * |
42 * |
43 * v
44 * +--------------------------*
45 * | Take a snapshot the data | <--------+
46 * +--------------------------+ |
47 * | |
48 * | |
49 * v |
50 * +----------------------+ |
51 * | Process the snapshot | |
52 * +----------------------+ |
53 * | |
54 * | |
55 * v |
56 * +------------------------------------+ |
57 * | Print the result of the processing | |
58 * +------------------------------------+ |
59 * | |
60 * | |
61 * v |
62 * Yes --------------- |
63 * +------------ < interval == 0 ? > |
64 * | --------------- |
65 * | | |
66 * | | No |
67 * | v |
68 * | +------------------------+ |
69 * | | Sleep for the duration | ----------+
70 * | | of the interval. |
71 * | +------------------------+
72 * |
73 * +---------------------+
74 * |
75 * v
76 *
77 * Exit
78 *
79 * There are two sets of snapshots. One set for the smbsrv module and the other
80 * for the task queue (SMB workers). Each set contains 2 snapshots. One is
81 * labeled 'current' the other one 'previous'. Their role changes after each
82 * snapshot. The 'current' becomes 'previous' and vice versa.
83 * The first snapshot taken is compared against the data gathered since the
84 * smbsrv module was loaded. Subsequent snapshots will be compared against the
85 * previous snapshot.
86 */
87
88 #include <stdio.h>
89 #include <stdlib.h>
90 #include <unistd.h>
91 #include <kstat.h>
92 #include <stdarg.h>
93 #include <errno.h>
94 #include <inttypes.h>
95 #include <strings.h>
96 #include <utility.h>
97 #include <libintl.h>
98 #include <zone.h>
99 #include <termios.h>
100 #include <stropts.h>
101 #include <math.h>
102 #include <umem.h>
103 #include <locale.h>
104 #include <smbsrv/smb_kstat.h>
105
106 #if !defined(TEXT_DOMAIN)
107 #define TEXT_DOMAIN "SYS_TEST"
108 #endif /* TEXT_DOMAIN */
109
110 #define SMBSTAT_ID_NO_CPU -1
111 #define SMBSTAT_SNAPSHOT_COUNT 2 /* Must be a power of 2 */
112 #define SMBSTAT_SNAPSHOT_MASK (SMBSTAT_SNAPSHOT_COUNT - 1)
113
114 #define SMBSTAT_HELP \
115 "Usage: smbstat [-acnrtuz] [interval]\n" \
116 " -c: display counters\n" \
117 " -t: display throughput\n" \
118 " -u: display utilization\n" \
119 " -r: display requests\n" \
120 " -a: all the requests (supported and unsupported)\n" \
121 " -z: skip the requests not received\n" \
122 " -n: display in alphabetic order\n" \
123 " interval: refresh cycle in seconds\n"
124
125 #define SMBSRV_COUNTERS_BANNER "\n nbt tcp users trees files pipes\n"
126 #define SMBSRV_COUNTERS_FORMAT "%5d %5d %5d %5d %5d %5d\n"
127
128 #define SMBSRV_THROUGHPUT_BANNER \
129 "\nrbytes/s tbytes/s reqs/s reads/s writes/s\n"
130 #define SMBSRV_THROUGHPUT_FORMAT \
131 "%1.3e %1.3e %1.3e %1.3e %1.3e\n"
132
133 #define SMBSRV_UTILIZATION_BANNER \
134 "\n wcnt rcnt wtime rtime" \
135 " w%% r%% u%% sat usr%% sys%% idle%%\n"
136 #define SMBSRV_UTILIZATION_FORMAT \
137 "%1.3e %1.3e %1.3e %1.3e %3.0f %3.0f %3.0f %s " \
138 "%3.0f %3.0f %3.0f\n"
139
140 #define SMBSRV_REQUESTS_BANNER \
141 "\n%30s code %% rbytes/s tbytes/s req/s rt-mean" \
142 " rt-stddev\n"
143 #define SMBSRV_REQUESTS_FORMAT \
144 "%30s %02X %3.0f %1.3e %1.3e %1.3e %1.3e %1.3e\n"
145
146 typedef enum {
147 CPU_TICKS_IDLE = 0,
148 CPU_TICKS_USER,
149 CPU_TICKS_KERNEL,
150 CPU_TICKS_SENTINEL
151 } cpu_state_idx_t;
152
153 typedef struct smbstat_cpu_snapshot {
154 processorid_t cs_id;
155 int cs_state;
156 uint64_t cs_ticks[CPU_TICKS_SENTINEL];
157 } smbstat_cpu_snapshot_t;
158
159 typedef struct smbstat_srv_snapshot {
160 hrtime_t ss_snaptime;
161 smbsrv_kstats_t ss_data;
162 } smbstat_srv_snapshot_t;
163
164 typedef struct smbstat_wrk_snapshot {
165 uint64_t ws_maxthreads;
166 uint64_t ws_bnalloc;
167 } smbstat_wrk_snapshot_t;
168
169 typedef struct smbstat_req_info {
170 char ri_name[KSTAT_STRLEN];
171 int ri_opcode;
172 double ri_pct;
173 double ri_tbs;
174 double ri_rbs;
175 double ri_rqs;
176 double ri_stddev;
177 double ri_mean;
178 } smbstat_req_info_t;
179
180 typedef struct smbstat_srv_info {
181 double si_hretime;
182 double si_etime;
183 double si_total_nreqs;
184 /*
185 * Counters
186 */
187 uint32_t si_nbt_sess; /* NBT sessions */
188 uint32_t si_tcp_sess; /* TCP sessions */
189 uint32_t si_users; /* Users logged in */
190 uint32_t si_trees; /* Trees connected */
191 uint32_t si_files; /* Open files */
192 uint32_t si_pipes; /* Open pipes */
193 /*
194 * Throughput of the server
195 */
196 double si_tbs; /* Bytes transmitted / second */
197 double si_rbs; /* Bytes received / second */
198 double si_rqs; /* Requests treated / second */
199 double si_rds; /* Reads treated / second */
200 double si_wrs; /* Writes treated / second */
201 /*
202 * Utilization of the server
203 */
204 double si_wpct; /* */
205 double si_rpct; /* */
206 double si_upct; /* Utilization in % */
207 double si_avw; /* Average number of requests waiting */
208 double si_avr; /* Average number of requests running */
209 double si_wserv; /* Average waiting time */
210 double si_rserv; /* Average running time */
211 boolean_t si_sat;
212 double si_ticks[CPU_TICKS_SENTINEL];
213 /*
214 * Latency & Throughput per request
215 */
216 smbstat_req_info_t si_reqs[SMB_COM_NUM];
217 } smbstat_srv_info_t;
218
219 static void smbstat_init(void);
220 static void smbstat_fini(void);
221 static void smbstat_kstat_snapshot(void);
222 static void smbstat_kstat_process(void);
223 static void smbstat_kstat_print(void);
224
225 static void smbstat_print_counters(void);
226 static void smbstat_print_throughput(void);
227 static void smbstat_print_utilization(void);
228 static void smbstat_print_requests(void);
229
230 static void smbstat_cpu_init(void);
231 static void smbstat_cpu_fini(void);
232 static smbstat_cpu_snapshot_t *smbstat_cpu_current_snapshot(void);
233 static smbstat_cpu_snapshot_t *smbstat_cpu_previous_snapshot(void);
234 static void smbstat_cpu_snapshot(void);
235 static void smbstat_cpu_process(void);
236
237 static void smbstat_wrk_init(void);
238 static void smbstat_wrk_fini(void);
239 static void smbstat_wrk_snapshot(void);
240 static void smbstat_wrk_process(void);
241 static smbstat_wrk_snapshot_t *smbstat_wrk_current_snapshot(void);
242
243 static void smbstat_srv_init(void);
244 static void smbstat_srv_fini(void);
245 static void smbstat_srv_snapshot(void);
246 static void smbstat_srv_process(void);
247 static void smbstat_srv_process_counters(smbstat_srv_snapshot_t *);
248 static void smbstat_srv_process_throughput(smbstat_srv_snapshot_t *,
249 smbstat_srv_snapshot_t *);
250 static void smbstat_srv_process_utilization(smbstat_srv_snapshot_t *,
251 smbstat_srv_snapshot_t *);
252 static void smbstat_srv_process_requests(smbstat_srv_snapshot_t *,
253 smbstat_srv_snapshot_t *);
254 static smbstat_srv_snapshot_t *smbstat_srv_current_snapshot(void);
255 static smbstat_srv_snapshot_t *smbstat_srv_previous_snapshot(void);
256
257 static void *smbstat_zalloc(size_t);
258 static void smbstat_free(void *, size_t);
259 static void smbstat_fail(int, char *, ...);
260 static void smbstat_snapshot_inc_idx(void);
261 static void smbstat_usage(FILE *, int);
262 static uint_t smbstat_strtoi(char const *, char *);
263 static double smbstat_hrtime_delta(hrtime_t, hrtime_t);
264 static double smbstat_sub_64(uint64_t, uint64_t);
265 static void smbstat_req_order(void);
266 static double smbstat_zero(double);
267 static void smbstat_termio_init(void);
268
269 #pragma does_not_return(smbstat_fail, smbstat_usage)
270
271 static char *smbstat_cpu_states[CPU_TICKS_SENTINEL] = {
272 "cpu_ticks_idle",
273 "cpu_ticks_user",
274 "cpu_ticks_kernel"
275 };
276
277 static boolean_t smbstat_opt_a = B_FALSE; /* all */
278 static boolean_t smbstat_opt_c = B_FALSE; /* counters */
279 static boolean_t smbstat_opt_n = B_FALSE; /* by name */
280 static boolean_t smbstat_opt_u = B_FALSE; /* utilization */
281 static boolean_t smbstat_opt_t = B_FALSE; /* throughput */
282 static boolean_t smbstat_opt_r = B_FALSE; /* requests */
283 static boolean_t smbstat_opt_z = B_FALSE; /* non-zero requests */
284
285 static uint_t smbstat_interval = 0;
286 static long smbstat_nrcpus = 0;
287 static kstat_ctl_t *smbstat_ksc = NULL;
288 static kstat_t *smbstat_srv_ksp = NULL;
289 static kstat_t *smbstat_wrk_ksp = NULL;
290 static struct winsize smbstat_ws;
291 static uint16_t smbstat_rows = 0;
292
293 static int smbstat_snapshot_idx = 0;
294 static smbstat_cpu_snapshot_t *smbstat_cpu_snapshots[SMBSTAT_SNAPSHOT_COUNT];
295 static smbstat_srv_snapshot_t smbstat_srv_snapshots[SMBSTAT_SNAPSHOT_COUNT];
296 static smbstat_wrk_snapshot_t smbstat_wrk_snapshots[SMBSTAT_SNAPSHOT_COUNT];
297 static smbstat_srv_info_t smbstat_srv_info;
298
299 /*
300 * main
301 */
302 int
main(int argc,char * argv[])303 main(int argc, char *argv[])
304 {
305 int c;
306
307 (void) setlocale(LC_ALL, "");
308 (void) textdomain(TEXT_DOMAIN);
309
310 if (getzoneid() != GLOBAL_ZONEID) {
311 (void) fprintf(stderr,
312 gettext("%s: Cannot execute in non-global zone.\n"),
313 argv[0]);
314 return (0);
315 }
316
317 if (is_system_labeled()) {
318 (void) fprintf(stderr,
319 gettext("%s: Trusted Extensions not supported.\n"),
320 argv[0]);
321 return (0);
322 }
323
324 while ((c = getopt(argc, argv, "achnrtuz")) != EOF) {
325 switch (c) {
326 case 'a':
327 smbstat_opt_a = B_TRUE;
328 break;
329 case 'n':
330 smbstat_opt_n = B_TRUE;
331 break;
332 case 'u':
333 smbstat_opt_u = B_TRUE;
334 break;
335 case 'c':
336 smbstat_opt_c = B_TRUE;
337 break;
338 case 'r':
339 smbstat_opt_r = B_TRUE;
340 break;
341 case 't':
342 smbstat_opt_t = B_TRUE;
343 break;
344 case 'z':
345 smbstat_opt_z = B_TRUE;
346 break;
347 case 'h':
348 smbstat_usage(stdout, 0);
349 default:
350 smbstat_usage(stderr, 1);
351 }
352 }
353
354 if (!smbstat_opt_u &&
355 !smbstat_opt_c &&
356 !smbstat_opt_r &&
357 !smbstat_opt_t) {
358 /* Default options when none is specified. */
359 smbstat_opt_u = B_TRUE;
360 smbstat_opt_t = B_TRUE;
361 }
362
363 if (optind < argc) {
364 smbstat_interval =
365 smbstat_strtoi(argv[optind], "invalid count");
366 optind++;
367 }
368
369 if ((argc - optind) > 1)
370 smbstat_usage(stderr, 1);
371
372 (void) atexit(smbstat_fini);
373 smbstat_init();
374 for (;;) {
375 smbstat_kstat_snapshot();
376 smbstat_kstat_process();
377 smbstat_kstat_print();
378 if (smbstat_interval == 0)
379 break;
380 (void) sleep(smbstat_interval);
381 smbstat_snapshot_inc_idx();
382 }
383 return (0);
384 }
385
386 /*
387 * smbstat_init
388 *
389 * Global initialization.
390 */
391 static void
smbstat_init(void)392 smbstat_init(void)
393 {
394 if ((smbstat_ksc = kstat_open()) == NULL)
395 smbstat_fail(1, gettext("kstat_open(): can't open /dev/kstat"));
396
397 smbstat_cpu_init();
398 smbstat_srv_init();
399 smbstat_wrk_init();
400 smbstat_req_order();
401 }
402
403 /*
404 * smbstat_fini
405 *
406 * Releases the resources smbstat_init() allocated.
407 */
408 static void
smbstat_fini(void)409 smbstat_fini(void)
410 {
411 smbstat_wrk_fini();
412 smbstat_srv_fini();
413 smbstat_cpu_fini();
414 (void) kstat_close(smbstat_ksc);
415 }
416
417 /*
418 * smbstat_kstat_snapshot
419 *
420 * Takes a snapshot of the data.
421 */
422 static void
smbstat_kstat_snapshot(void)423 smbstat_kstat_snapshot(void)
424 {
425 smbstat_cpu_snapshot();
426 smbstat_srv_snapshot();
427 smbstat_wrk_snapshot();
428 }
429
430 /*
431 * smbstat_kstat_process
432 */
433 static void
smbstat_kstat_process(void)434 smbstat_kstat_process(void)
435 {
436 smbstat_cpu_process();
437 smbstat_srv_process();
438 smbstat_wrk_process();
439 }
440
441 /*
442 * smbstat_kstat_print
443 *
444 * Print the data processed.
445 */
446 static void
smbstat_kstat_print(void)447 smbstat_kstat_print(void)
448 {
449 smbstat_termio_init();
450 smbstat_print_counters();
451 smbstat_print_throughput();
452 smbstat_print_utilization();
453 smbstat_print_requests();
454 }
455
456 /*
457 * smbstat_print_counters
458 *
459 * Displays the SMB server counters (session, users...).
460 */
461 static void
smbstat_print_counters(void)462 smbstat_print_counters(void)
463 {
464 if (!smbstat_opt_c)
465 return;
466
467 if (smbstat_opt_u || smbstat_opt_r || smbstat_opt_t ||
468 (smbstat_rows == 0) || (smbstat_rows >= smbstat_ws.ws_row)) {
469 (void) printf(SMBSRV_COUNTERS_BANNER);
470 smbstat_rows = 1;
471 }
472
473 (void) printf(SMBSRV_COUNTERS_FORMAT,
474 smbstat_srv_info.si_nbt_sess,
475 smbstat_srv_info.si_tcp_sess,
476 smbstat_srv_info.si_users,
477 smbstat_srv_info.si_trees,
478 smbstat_srv_info.si_files,
479 smbstat_srv_info.si_pipes);
480
481 ++smbstat_rows;
482 }
483 /*
484 * smbstat_print_throughput
485 *
486 * Formats the SMB server throughput output.
487 */
488 static void
smbstat_print_throughput(void)489 smbstat_print_throughput(void)
490 {
491 if (!smbstat_opt_t)
492 return;
493
494 if (smbstat_opt_u || smbstat_opt_r || smbstat_opt_c ||
495 (smbstat_rows == 0) || (smbstat_rows >= smbstat_ws.ws_row)) {
496 (void) printf(SMBSRV_THROUGHPUT_BANNER);
497 smbstat_rows = 1;
498 }
499 (void) printf(SMBSRV_THROUGHPUT_FORMAT,
500 smbstat_zero(smbstat_srv_info.si_rbs),
501 smbstat_zero(smbstat_srv_info.si_tbs),
502 smbstat_zero(smbstat_srv_info.si_rqs),
503 smbstat_zero(smbstat_srv_info.si_rds),
504 smbstat_zero(smbstat_srv_info.si_wrs));
505
506 ++smbstat_rows;
507 }
508
509 /*
510 * smbstat_print_utilization
511 */
512 static void
smbstat_print_utilization(void)513 smbstat_print_utilization(void)
514 {
515 char *sat;
516 if (!smbstat_opt_u)
517 return;
518
519 if (smbstat_opt_t || smbstat_opt_r || smbstat_opt_c ||
520 (smbstat_rows == 0) || (smbstat_rows >= smbstat_ws.ws_row)) {
521 (void) printf(SMBSRV_UTILIZATION_BANNER);
522 smbstat_rows = 1;
523 }
524
525 if (smbstat_srv_info.si_sat)
526 sat = "yes";
527 else
528 sat = "no ";
529
530 (void) printf(SMBSRV_UTILIZATION_FORMAT,
531 smbstat_srv_info.si_avw,
532 smbstat_srv_info.si_avr,
533 smbstat_srv_info.si_wserv,
534 smbstat_srv_info.si_rserv,
535 smbstat_zero(smbstat_srv_info.si_wpct),
536 smbstat_zero(smbstat_srv_info.si_rpct),
537 smbstat_zero(smbstat_srv_info.si_upct),
538 sat,
539 smbstat_srv_info.si_ticks[CPU_TICKS_USER],
540 smbstat_srv_info.si_ticks[CPU_TICKS_KERNEL],
541 smbstat_srv_info.si_ticks[CPU_TICKS_IDLE]);
542
543 ++smbstat_rows;
544 }
545
546 /*
547 * smbstat_print_requests
548 */
549 static void
smbstat_print_requests(void)550 smbstat_print_requests(void)
551 {
552 smbstat_req_info_t *prq;
553 int i;
554
555 if (!smbstat_opt_r)
556 return;
557
558 prq = smbstat_srv_info.si_reqs;
559
560 (void) printf(SMBSRV_REQUESTS_BANNER, " ");
561
562 for (i = 0; i < SMB_COM_NUM; i++) {
563 if (!smbstat_opt_a &&
564 strncmp(prq[i].ri_name, "Invalid", sizeof ("Invalid")) == 0)
565 continue;
566
567 if (!smbstat_opt_z || (prq[i].ri_pct != 0)) {
568 (void) printf(SMBSRV_REQUESTS_FORMAT,
569 prq[i].ri_name,
570 prq[i].ri_opcode,
571 smbstat_zero(prq[i].ri_pct),
572 smbstat_zero(prq[i].ri_rbs),
573 smbstat_zero(prq[i].ri_tbs),
574 smbstat_zero(prq[i].ri_rqs),
575 prq[i].ri_mean,
576 prq[i].ri_stddev);
577 }
578 }
579 }
580
581 /*
582 * smbstat_cpu_init
583 */
584 static void
smbstat_cpu_init(void)585 smbstat_cpu_init(void)
586 {
587 size_t size;
588 int i;
589
590 smbstat_nrcpus = sysconf(_SC_CPUID_MAX) + 1;
591 size = smbstat_nrcpus * sizeof (smbstat_cpu_snapshot_t);
592
593 for (i = 0; i < SMBSTAT_SNAPSHOT_COUNT; i++)
594 smbstat_cpu_snapshots[i] = smbstat_zalloc(size);
595 }
596
597 /*
598 * smbstat_cpu_fini
599 */
600 static void
smbstat_cpu_fini(void)601 smbstat_cpu_fini(void)
602 {
603 size_t size;
604 int i;
605
606 size = smbstat_nrcpus * sizeof (smbstat_cpu_snapshot_t);
607
608 for (i = 0; i < SMBSTAT_SNAPSHOT_COUNT; i++)
609 smbstat_free(smbstat_cpu_snapshots[i], size);
610 }
611
612 /*
613 * smbstat_cpu_current_snapshot
614 */
615 static smbstat_cpu_snapshot_t *
smbstat_cpu_current_snapshot(void)616 smbstat_cpu_current_snapshot(void)
617 {
618 return (smbstat_cpu_snapshots[smbstat_snapshot_idx]);
619 }
620
621 /*
622 * smbstat_cpu_previous_snapshot
623 */
624 static smbstat_cpu_snapshot_t *
smbstat_cpu_previous_snapshot(void)625 smbstat_cpu_previous_snapshot(void)
626 {
627 int idx;
628
629 idx = (smbstat_snapshot_idx - 1) & SMBSTAT_SNAPSHOT_MASK;
630 return (smbstat_cpu_snapshots[idx]);
631 }
632
633 /*
634 * smbstat_cpu_snapshot
635 */
636 static void
smbstat_cpu_snapshot(void)637 smbstat_cpu_snapshot(void)
638 {
639 kstat_t *ksp;
640 kstat_named_t *ksn;
641 smbstat_cpu_snapshot_t *curr;
642 long i;
643 int j;
644
645 curr = smbstat_cpu_current_snapshot();
646
647 for (i = 0; i < smbstat_nrcpus; i++, curr++) {
648 curr->cs_id = SMBSTAT_ID_NO_CPU;
649 curr->cs_state = p_online(i, P_STATUS);
650 /* If no valid CPU is present, move on to the next one */
651 if (curr->cs_state == -1)
652 continue;
653
654 curr->cs_id = i;
655
656 ksp = kstat_lookup(smbstat_ksc, "cpu", i, "sys");
657 if (ksp == NULL)
658 smbstat_fail(1,
659 gettext("kstat_lookup('cpu sys %d') failed"), i);
660
661 if (kstat_read(smbstat_ksc, ksp, NULL) == -1)
662 smbstat_fail(1,
663 gettext("kstat_read('cpu sys %d') failed"), i);
664
665 for (j = 0; j < CPU_TICKS_SENTINEL; j++) {
666 ksn = kstat_data_lookup(ksp, smbstat_cpu_states[j]);
667 if (ksn == NULL)
668 smbstat_fail(1,
669 gettext("kstat_data_lookup('%s') failed"),
670 smbstat_cpu_states[j]);
671 curr->cs_ticks[j] = ksn->value.ui64;
672 }
673 }
674 }
675
676 /*
677 * smbstat_cpu_process
678 */
679 static void
smbstat_cpu_process(void)680 smbstat_cpu_process(void)
681 {
682 smbstat_cpu_snapshot_t *curr, *prev;
683 double total_ticks;
684 double agg_ticks[CPU_TICKS_SENTINEL];
685 int i, j;
686
687 curr = smbstat_cpu_current_snapshot();
688 prev = smbstat_cpu_previous_snapshot();
689 bzero(agg_ticks, sizeof (agg_ticks));
690 total_ticks = 0;
691
692 for (i = 0; i < smbstat_nrcpus; i++, curr++, prev++) {
693 for (j = 0; j < CPU_TICKS_SENTINEL; j++) {
694 agg_ticks[j] += smbstat_sub_64(curr->cs_ticks[j],
695 prev->cs_ticks[j]);
696 total_ticks += smbstat_sub_64(curr->cs_ticks[j],
697 prev->cs_ticks[j]);
698 }
699 }
700
701 for (j = 0; j < CPU_TICKS_SENTINEL; j++)
702 smbstat_srv_info.si_ticks[j] =
703 (agg_ticks[j] * 100.0) / total_ticks;
704 }
705
706 /*
707 * smbstat_wrk_init
708 */
709 static void
smbstat_wrk_init(void)710 smbstat_wrk_init(void)
711 {
712 smbstat_wrk_ksp =
713 kstat_lookup(smbstat_ksc, "unix", -1, SMBSRV_KSTAT_WORKERS);
714 if (smbstat_wrk_ksp == NULL)
715 smbstat_fail(1,
716 gettext("cannot retrieve smbsrv workers kstat\n"));
717 }
718
719 static void
smbstat_wrk_fini(void)720 smbstat_wrk_fini(void)
721 {
722 smbstat_wrk_ksp = NULL;
723 }
724
725 /*
726 * smbstat_wrk_snapshot
727 */
728 static void
smbstat_wrk_snapshot(void)729 smbstat_wrk_snapshot(void)
730 {
731 smbstat_wrk_snapshot_t *curr;
732 kstat_named_t *kn;
733
734 curr = smbstat_wrk_current_snapshot();
735
736 if (kstat_read(smbstat_ksc, smbstat_wrk_ksp, NULL) == -1)
737 smbstat_fail(1, gettext("kstat_read('%s') failed"),
738 smbstat_wrk_ksp->ks_name);
739
740 kn = kstat_data_lookup(smbstat_wrk_ksp, "maxthreads");
741 if ((kn == NULL) || (kn->data_type != KSTAT_DATA_UINT64))
742 smbstat_fail(1, gettext("kstat_read('%s') failed"),
743 "maxthreads");
744 curr->ws_maxthreads = kn->value.ui64;
745
746 kn = kstat_data_lookup(smbstat_wrk_ksp, "bnalloc");
747 if ((kn == NULL) || (kn->data_type != KSTAT_DATA_UINT64))
748 smbstat_fail(1, gettext("kstat_read('%s') failed"),
749 "bnalloc");
750 curr->ws_bnalloc = kn->value.ui64;
751 }
752
753 /*
754 * smbstat_wrk_process
755 */
756 static void
smbstat_wrk_process(void)757 smbstat_wrk_process(void)
758 {
759 smbstat_wrk_snapshot_t *curr;
760
761 curr = smbstat_wrk_current_snapshot();
762
763 if (curr->ws_maxthreads >= curr->ws_bnalloc)
764 smbstat_srv_info.si_sat = B_TRUE;
765 else
766 smbstat_srv_info.si_sat = B_FALSE;
767 }
768
769 /*
770 * smbstat_wrk_current_snapshot
771 */
772 static smbstat_wrk_snapshot_t *
smbstat_wrk_current_snapshot(void)773 smbstat_wrk_current_snapshot(void)
774 {
775 return (&smbstat_wrk_snapshots[smbstat_snapshot_idx]);
776 }
777
778 /*
779 * smbstat_srv_init
780 */
781 static void
smbstat_srv_init(void)782 smbstat_srv_init(void)
783 {
784 smbstat_srv_ksp = kstat_lookup(smbstat_ksc, SMBSRV_KSTAT_MODULE,
785 getzoneid(), SMBSRV_KSTAT_STATISTICS);
786 if (smbstat_srv_ksp == NULL)
787 smbstat_fail(1, gettext("cannot retrieve smbsrv kstat\n"));
788 }
789
790 /*
791 * smbstat_srv_fini
792 */
793 static void
smbstat_srv_fini(void)794 smbstat_srv_fini(void)
795 {
796 smbstat_srv_ksp = NULL;
797 }
798
799 /*
800 * smbstat_srv_snapshot
801 *
802 * Take a snapshot of the smbsrv module statistics.
803 */
804 static void
smbstat_srv_snapshot(void)805 smbstat_srv_snapshot(void)
806 {
807 smbstat_srv_snapshot_t *curr;
808
809 curr = smbstat_srv_current_snapshot();
810
811 if ((kstat_read(smbstat_ksc, smbstat_srv_ksp, NULL) == -1) ||
812 (smbstat_srv_ksp->ks_data_size != sizeof (curr->ss_data)))
813 smbstat_fail(1, gettext("kstat_read('%s') failed"),
814 smbstat_srv_ksp->ks_name);
815
816 curr->ss_snaptime = smbstat_srv_ksp->ks_snaptime;
817 bcopy(smbstat_srv_ksp->ks_data, &curr->ss_data, sizeof (curr->ss_data));
818 }
819
820 /*
821 * smbstat_srv_process
822 *
823 * Processes the snapshot data.
824 */
825 static void
smbstat_srv_process(void)826 smbstat_srv_process(void)
827 {
828 smbstat_srv_snapshot_t *curr, *prev;
829
830 curr = smbstat_srv_current_snapshot();
831 prev = smbstat_srv_previous_snapshot();
832
833 if (prev->ss_snaptime == 0)
834 smbstat_srv_info.si_hretime =
835 smbstat_hrtime_delta(curr->ss_data.ks_start_time,
836 curr->ss_snaptime);
837 else
838 smbstat_srv_info.si_hretime =
839 smbstat_hrtime_delta(prev->ss_snaptime, curr->ss_snaptime);
840
841 smbstat_srv_info.si_etime = smbstat_srv_info.si_hretime / NANOSEC;
842 smbstat_srv_info.si_total_nreqs =
843 smbstat_sub_64(curr->ss_data.ks_nreq, prev->ss_data.ks_nreq);
844
845 if (smbstat_opt_c)
846 smbstat_srv_process_counters(curr);
847 if (smbstat_opt_t)
848 smbstat_srv_process_throughput(curr, prev);
849 if (smbstat_opt_u)
850 smbstat_srv_process_utilization(curr, prev);
851 if (smbstat_opt_r)
852 smbstat_srv_process_requests(curr, prev);
853 }
854
855 /*
856 * smbstat_srv_process_counters
857 */
858 static void
smbstat_srv_process_counters(smbstat_srv_snapshot_t * curr)859 smbstat_srv_process_counters(smbstat_srv_snapshot_t *curr)
860 {
861 smbstat_srv_info.si_nbt_sess = curr->ss_data.ks_nbt_sess;
862 smbstat_srv_info.si_tcp_sess = curr->ss_data.ks_tcp_sess;
863 smbstat_srv_info.si_users = curr->ss_data.ks_users;
864 smbstat_srv_info.si_trees = curr->ss_data.ks_trees;
865 smbstat_srv_info.si_files = curr->ss_data.ks_files;
866 smbstat_srv_info.si_pipes = curr->ss_data.ks_pipes;
867 }
868
869 /*
870 * smbstat_srv_process_throughput
871 *
872 * Processes the data relative to the throughput of the smbsrv module and
873 * stores the results in the structure smbstat_srv_info.
874 */
875 static void
smbstat_srv_process_throughput(smbstat_srv_snapshot_t * curr,smbstat_srv_snapshot_t * prev)876 smbstat_srv_process_throughput(
877 smbstat_srv_snapshot_t *curr,
878 smbstat_srv_snapshot_t *prev)
879 {
880 smbstat_srv_info.si_tbs =
881 smbstat_sub_64(curr->ss_data.ks_txb, prev->ss_data.ks_txb);
882 smbstat_srv_info.si_tbs /= smbstat_srv_info.si_etime;
883 smbstat_srv_info.si_rbs =
884 smbstat_sub_64(curr->ss_data.ks_rxb, prev->ss_data.ks_rxb);
885 smbstat_srv_info.si_rbs /= smbstat_srv_info.si_etime;
886 smbstat_srv_info.si_rqs = smbstat_srv_info.si_total_nreqs;
887 smbstat_srv_info.si_rqs /= smbstat_srv_info.si_etime;
888
889 smbstat_srv_info.si_rds = smbstat_sub_64(
890 curr->ss_data.ks_reqs[SMB_COM_READ].kr_nreq,
891 prev->ss_data.ks_reqs[SMB_COM_READ].kr_nreq);
892 smbstat_srv_info.si_rds += smbstat_sub_64(
893 curr->ss_data.ks_reqs[SMB_COM_LOCK_AND_READ].kr_nreq,
894 prev->ss_data.ks_reqs[SMB_COM_LOCK_AND_READ].kr_nreq);
895 smbstat_srv_info.si_rds += smbstat_sub_64(
896 curr->ss_data.ks_reqs[SMB_COM_READ_RAW].kr_nreq,
897 prev->ss_data.ks_reqs[SMB_COM_READ_RAW].kr_nreq);
898 smbstat_srv_info.si_rds += smbstat_sub_64(
899 curr->ss_data.ks_reqs[SMB_COM_READ_ANDX].kr_nreq,
900 prev->ss_data.ks_reqs[SMB_COM_READ_ANDX].kr_nreq);
901 smbstat_srv_info.si_rds /= smbstat_srv_info.si_etime;
902
903 smbstat_srv_info.si_wrs = smbstat_sub_64(
904 curr->ss_data.ks_reqs[SMB_COM_WRITE].kr_nreq,
905 prev->ss_data.ks_reqs[SMB_COM_WRITE].kr_nreq);
906 smbstat_srv_info.si_wrs += smbstat_sub_64(
907 curr->ss_data.ks_reqs[SMB_COM_WRITE_AND_UNLOCK].kr_nreq,
908 prev->ss_data.ks_reqs[SMB_COM_WRITE_AND_UNLOCK].kr_nreq);
909 smbstat_srv_info.si_wrs += smbstat_sub_64(
910 curr->ss_data.ks_reqs[SMB_COM_WRITE_RAW].kr_nreq,
911 prev->ss_data.ks_reqs[SMB_COM_WRITE_RAW].kr_nreq);
912 smbstat_srv_info.si_wrs += smbstat_sub_64(
913 curr->ss_data.ks_reqs[SMB_COM_WRITE_AND_CLOSE].kr_nreq,
914 prev->ss_data.ks_reqs[SMB_COM_WRITE_AND_CLOSE].kr_nreq);
915 smbstat_srv_info.si_wrs += smbstat_sub_64(
916 curr->ss_data.ks_reqs[SMB_COM_WRITE_ANDX].kr_nreq,
917 prev->ss_data.ks_reqs[SMB_COM_WRITE_ANDX].kr_nreq);
918 smbstat_srv_info.si_wrs /= smbstat_srv_info.si_etime;
919 }
920
921 /*
922 * smbstat_srv_process_utilization
923 *
924 * Processes the data relative to the utilization of the smbsrv module and
925 * stores the results in the structure smbstat_srv_info.
926 */
927 static void
smbstat_srv_process_utilization(smbstat_srv_snapshot_t * curr,smbstat_srv_snapshot_t * prev)928 smbstat_srv_process_utilization(
929 smbstat_srv_snapshot_t *curr,
930 smbstat_srv_snapshot_t *prev)
931 {
932 double tw_delta, tr_delta;
933 double w_delta, r_delta;
934 double tps, rqs;
935
936 w_delta = smbstat_hrtime_delta(prev->ss_data.ks_utilization.ku_wlentime,
937 curr->ss_data.ks_utilization.ku_wlentime);
938 r_delta = smbstat_hrtime_delta(prev->ss_data.ks_utilization.ku_rlentime,
939 curr->ss_data.ks_utilization.ku_rlentime);
940 tw_delta = smbstat_hrtime_delta(prev->ss_data.ks_utilization.ku_wtime,
941 curr->ss_data.ks_utilization.ku_wtime);
942 tr_delta = smbstat_hrtime_delta(prev->ss_data.ks_utilization.ku_rtime,
943 curr->ss_data.ks_utilization.ku_rtime);
944 rqs = smbstat_srv_info.si_total_nreqs / smbstat_srv_info.si_etime;
945
946 /* Average number of requests waiting */
947 if (w_delta != 0)
948 smbstat_srv_info.si_avw = w_delta / smbstat_srv_info.si_hretime;
949 else
950 smbstat_srv_info.si_avw = 0.0;
951
952 /* Average number of request running */
953 if (r_delta != 0)
954 smbstat_srv_info.si_avr = r_delta / smbstat_srv_info.si_hretime;
955 else
956 smbstat_srv_info.si_avr = 0.0;
957
958 /* Utilization */
959 smbstat_srv_info.si_upct =
960 (smbstat_srv_info.si_avr / curr->ss_data.ks_maxreqs) * 100;
961
962 /* Average wait service time in milliseconds */
963 smbstat_srv_info.si_rserv = 0.0;
964 smbstat_srv_info.si_wserv = 0.0;
965 if (rqs > 0.0 &&
966 (smbstat_srv_info.si_avw != 0.0 ||
967 smbstat_srv_info.si_avr != 0.0)) {
968 tps = 1 / rqs;
969 if (smbstat_srv_info.si_avw != 0.0)
970 smbstat_srv_info.si_wserv =
971 smbstat_srv_info.si_avw * tps;
972 if (smbstat_srv_info.si_avr != 0.0)
973 smbstat_srv_info.si_rserv =
974 smbstat_srv_info.si_avr * tps;
975 }
976
977 /* % of time there is a transaction waiting for service */
978 if (tw_delta != 0) {
979 smbstat_srv_info.si_wpct = tw_delta;
980 smbstat_srv_info.si_wpct /= smbstat_srv_info.si_hretime;
981 smbstat_srv_info.si_wpct *= 100.0;
982 } else {
983 smbstat_srv_info.si_wpct = 0.0;
984 }
985
986 /* % of time there is a transaction running */
987 if (tr_delta != 0) {
988 smbstat_srv_info.si_rpct = tr_delta;
989 smbstat_srv_info.si_rpct /= smbstat_srv_info.si_hretime;
990 smbstat_srv_info.si_rpct *= 100.0;
991 } else {
992 smbstat_srv_info.si_rpct = 0.0;
993 }
994 }
995
996 /*
997 * smbstat_srv_process_requests
998 *
999 * Processes the data relative to the SMB requests and stores the results in
1000 * the structure smbstat_srv_info.
1001 */
1002 static void
smbstat_srv_process_requests(smbstat_srv_snapshot_t * curr,smbstat_srv_snapshot_t * prev)1003 smbstat_srv_process_requests(
1004 smbstat_srv_snapshot_t *curr,
1005 smbstat_srv_snapshot_t *prev)
1006 {
1007 smbstat_req_info_t *info;
1008 double nrqs;
1009 int i, idx;
1010
1011 info = smbstat_srv_info.si_reqs;
1012
1013 for (i = 0; i < SMB_COM_NUM; i++) {
1014 idx = info[i].ri_opcode;
1015
1016 nrqs = smbstat_sub_64(curr->ss_data.ks_reqs[idx].kr_nreq,
1017 prev->ss_data.ks_reqs[idx].kr_nreq);
1018
1019 info[i].ri_rqs = nrqs / smbstat_srv_info.si_etime;
1020
1021 info[i].ri_rbs = smbstat_sub_64(
1022 curr->ss_data.ks_reqs[idx].kr_rxb,
1023 prev->ss_data.ks_reqs[idx].kr_rxb) /
1024 smbstat_srv_info.si_etime;
1025
1026 info[i].ri_tbs = smbstat_sub_64(
1027 curr->ss_data.ks_reqs[idx].kr_txb,
1028 prev->ss_data.ks_reqs[idx].kr_txb) /
1029 smbstat_srv_info.si_etime;
1030
1031 info[i].ri_pct = nrqs * 100;
1032 if (smbstat_srv_info.si_total_nreqs > 0)
1033 info[i].ri_pct /= smbstat_srv_info.si_total_nreqs;
1034
1035 if (prev->ss_snaptime == 0) {
1036 /* First time. Take the aggregate */
1037 info[i].ri_stddev =
1038 curr->ss_data.ks_reqs[idx].kr_a_stddev;
1039 info[i].ri_mean = curr->ss_data.ks_reqs[idx].kr_a_mean;
1040 } else {
1041 /* Take the differential */
1042 info[i].ri_stddev =
1043 curr->ss_data.ks_reqs[idx].kr_d_stddev;
1044 info[i].ri_mean = curr->ss_data.ks_reqs[idx].kr_d_mean;
1045 }
1046 if (nrqs > 0) {
1047 info[i].ri_stddev /= nrqs;
1048 info[i].ri_stddev = sqrt(info[i].ri_stddev);
1049 } else {
1050 info[i].ri_stddev = 0;
1051 }
1052 info[i].ri_stddev /= NANOSEC;
1053 info[i].ri_mean /= NANOSEC;
1054 }
1055 }
1056
1057 /*
1058 * smbstat_srv_current_snapshot
1059 *
1060 * Returns the current snapshot.
1061 */
1062 static smbstat_srv_snapshot_t *
smbstat_srv_current_snapshot(void)1063 smbstat_srv_current_snapshot(void)
1064 {
1065 return (&smbstat_srv_snapshots[smbstat_snapshot_idx]);
1066 }
1067
1068 /*
1069 * smbstat_srv_previous_snapshot
1070 *
1071 * Returns the previous snapshot.
1072 */
1073 static smbstat_srv_snapshot_t *
smbstat_srv_previous_snapshot(void)1074 smbstat_srv_previous_snapshot(void)
1075 {
1076 int idx;
1077
1078 idx = (smbstat_snapshot_idx - 1) & SMBSTAT_SNAPSHOT_MASK;
1079 return (&smbstat_srv_snapshots[idx]);
1080 }
1081
1082 /*
1083 * smbstat_usage
1084 *
1085 * Prints out a help message.
1086 */
1087 static void
smbstat_usage(FILE * fd,int exit_code)1088 smbstat_usage(FILE *fd, int exit_code)
1089 {
1090 (void) fprintf(fd, gettext(SMBSTAT_HELP));
1091 exit(exit_code);
1092 }
1093
1094 /*
1095 * smbstat_fail
1096 *
1097 * Prints out to stderr an error message and exits the process.
1098 */
1099 static void
smbstat_fail(int do_perror,char * message,...)1100 smbstat_fail(int do_perror, char *message, ...)
1101 {
1102 va_list args;
1103
1104 va_start(args, message);
1105 (void) fprintf(stderr, gettext("smbstat: "));
1106 /* LINTED E_SEC_PRINTF_VAR_FMT */
1107 (void) vfprintf(stderr, message, args);
1108 va_end(args);
1109 if (do_perror)
1110 (void) fprintf(stderr, ": %s", strerror(errno));
1111 (void) fprintf(stderr, "\n");
1112 exit(1);
1113 }
1114
1115 /*
1116 * smbstat_sub_64
1117 *
1118 * Substract 2 uint64_t and returns a double.
1119 */
1120 static double
smbstat_sub_64(uint64_t a,uint64_t b)1121 smbstat_sub_64(uint64_t a, uint64_t b)
1122 {
1123 return ((double)(a - b));
1124 }
1125
1126 /*
1127 * smbstat_zero
1128 *
1129 * Returns zero if the value passed in is less than 1.
1130 */
1131 static double
smbstat_zero(double value)1132 smbstat_zero(double value)
1133 {
1134 if (value < 1)
1135 value = 0;
1136 return (value);
1137 }
1138
1139 /*
1140 * smbstat_strtoi
1141 *
1142 * Converts a string representing an integer value into its binary value.
1143 * If the conversion fails this routine exits the process.
1144 */
1145 static uint_t
smbstat_strtoi(char const * val,char * errmsg)1146 smbstat_strtoi(char const *val, char *errmsg)
1147 {
1148 char *end;
1149 long tmp;
1150
1151 errno = 0;
1152 tmp = strtol(val, &end, 10);
1153 if (*end != '\0' || errno)
1154 smbstat_fail(1, "%s %s", errmsg, val);
1155 return ((uint_t)tmp);
1156 }
1157
1158 /*
1159 * smbstat_termio_init
1160 *
1161 * Determines the size of the terminal associated with the process.
1162 */
1163 static void
smbstat_termio_init(void)1164 smbstat_termio_init(void)
1165 {
1166 char *envp;
1167
1168 if (ioctl(STDOUT_FILENO, TIOCGWINSZ, &smbstat_ws) != -1) {
1169 if (smbstat_ws.ws_row == 0) {
1170 envp = getenv("LINES");
1171 if (envp != NULL)
1172 smbstat_ws.ws_row = atoi(envp);
1173 }
1174
1175 if (smbstat_ws.ws_col == 0) {
1176 envp = getenv("COLUMNS");
1177 if (envp != NULL)
1178 smbstat_ws.ws_row = atoi(envp);
1179 }
1180 }
1181 if (smbstat_ws.ws_col == 0)
1182 smbstat_ws.ws_col = 80;
1183 if (smbstat_ws.ws_row == 0)
1184 smbstat_ws.ws_row = 25;
1185 }
1186
1187 /*
1188 * smbstat_snapshot_idx_inc
1189 *
1190 * Increments the snapshot index.
1191 */
1192 static void
smbstat_snapshot_inc_idx(void)1193 smbstat_snapshot_inc_idx(void)
1194 {
1195 smbstat_snapshot_idx++;
1196 smbstat_snapshot_idx &= SMBSTAT_SNAPSHOT_MASK;
1197 }
1198
1199 /*
1200 * smbstat_req_cmp_name
1201 *
1202 * Call back function passed to qsort() when the list of requests must be sorted
1203 * by name.
1204 */
1205 static int
smbstat_req_cmp_name(const void * obj1,const void * obj2)1206 smbstat_req_cmp_name(const void *obj1, const void *obj2)
1207 {
1208 return (strncasecmp(
1209 ((smbstat_req_info_t *)obj1)->ri_name,
1210 ((smbstat_req_info_t *)obj2)->ri_name,
1211 sizeof (((smbstat_req_info_t *)obj2)->ri_name)));
1212 }
1213
1214 /*
1215 * smbstat_req_order
1216 *
1217 * Snapshots the smbsrv module statistics once to get the name of the requests.
1218 * The request list is smbstat_srv_info is then sorted by name or by code
1219 * depending on the boolean smbstat_opt_a.
1220 * The function should be called once during initialization.
1221 */
1222 static void
smbstat_req_order(void)1223 smbstat_req_order(void)
1224 {
1225 smbstat_req_info_t *info;
1226 smb_kstat_req_t *reqs;
1227 int i;
1228
1229 smbstat_srv_snapshot();
1230 reqs = smbstat_srv_current_snapshot()->ss_data.ks_reqs;
1231 info = smbstat_srv_info.si_reqs;
1232
1233 for (i = 0; i < SMB_COM_NUM; i++) {
1234 (void) strlcpy(info[i].ri_name, reqs[i].kr_name,
1235 sizeof (reqs[i].kr_name));
1236 info[i].ri_opcode = i;
1237 }
1238 if (smbstat_opt_n)
1239 qsort(info, SMB_COM_NUM, sizeof (smbstat_req_info_t),
1240 smbstat_req_cmp_name);
1241 }
1242
1243 /*
1244 * Return the number of ticks delta between two hrtime_t
1245 * values. Attempt to cater for various kinds of overflow
1246 * in hrtime_t - no matter how improbable.
1247 */
1248 static double
smbstat_hrtime_delta(hrtime_t old,hrtime_t new)1249 smbstat_hrtime_delta(hrtime_t old, hrtime_t new)
1250 {
1251 uint64_t del;
1252
1253 if ((new >= old) && (old >= 0L))
1254 return ((double)(new - old));
1255 /*
1256 * We've overflowed the positive portion of an hrtime_t.
1257 */
1258 if (new < 0L) {
1259 /*
1260 * The new value is negative. Handle the case where the old
1261 * value is positive or negative.
1262 */
1263 uint64_t n1;
1264 uint64_t o1;
1265
1266 n1 = -new;
1267 if (old > 0L)
1268 return ((double)(n1 - old));
1269
1270 o1 = -old;
1271 del = n1 - o1;
1272 return ((double)del);
1273 }
1274
1275 /*
1276 * Either we've just gone from being negative to positive *or* the last
1277 * entry was positive and the new entry is also positive but *less* than
1278 * the old entry. This implies we waited quite a few days on a very fast
1279 * system between displays.
1280 */
1281 if (old < 0L) {
1282 uint64_t o2;
1283 o2 = -old;
1284 del = UINT64_MAX - o2;
1285 } else {
1286 del = UINT64_MAX - old;
1287 }
1288 del += new;
1289 return ((double)del);
1290 }
1291
1292 static void *
smbstat_zalloc(size_t size)1293 smbstat_zalloc(size_t size)
1294 {
1295 void *ptr;
1296
1297 ptr = umem_zalloc(size, UMEM_DEFAULT);
1298 if (ptr == NULL)
1299 smbstat_fail(1, gettext("out of memory"));
1300 return (ptr);
1301 }
1302
1303 static void
smbstat_free(void * ptr,size_t size)1304 smbstat_free(void *ptr, size_t size)
1305 {
1306 umem_free(ptr, size);
1307 }
1308