This is an automated email from the ASF dual-hosted git repository. xiaoxiang pushed a commit to branch master in repository https://gitbox.apache.org/repos/asf/nuttx-apps.git
The following commit(s) were added to refs/heads/master by this push: new 880e2661d apps/testing: Introduce timerjitter test case 880e2661d is described below commit 880e2661dca1f36b2b5743acb2e0db6f3a984303 Author: ouyangxiangzhen <ouyangxiangz...@xiaomi.com> AuthorDate: Wed Jun 12 19:56:09 2024 +0800 apps/testing: Introduce timerjitter test case This commit introduces the timerjitter test case, designed for assessing timer accuracy quickly. It is a simplified cyclictest implementation, optimized for fast and effective testing of timer precision. The program sets up a periodic timer and awaits the firing of SIGALRM. Upon signal arrived, it computes the time difference between consecutive periods. A smaller time difference indicates higher time accuracy. You can execute timerjitter without passing any arguments. Alternatively, you have the option to pass '-p' to print the time difference after each period, or provide additional arguments to customize the timer interval (in microseconds) and the number of test iterations. Signed-off-by: ouyangxiangzhen <ouyangxiangz...@xiaomi.com> --- testing/timerjitter/CMakeLists.txt | 41 +++++ testing/timerjitter/Kconfig | 22 +++ testing/timerjitter/Make.defs | 25 +++ testing/timerjitter/Makefile | 26 +++ testing/timerjitter/timerjitter.c | 338 +++++++++++++++++++++++++++++++++++++ 5 files changed, 452 insertions(+) diff --git a/testing/timerjitter/CMakeLists.txt b/testing/timerjitter/CMakeLists.txt new file mode 100644 index 000000000..ccfd9f6c3 --- /dev/null +++ b/testing/timerjitter/CMakeLists.txt @@ -0,0 +1,41 @@ +# ############################################################################## +# apps/testing/timerjitter/CMakeLists.txt +# +# Copyright (C) 2024 Xiaomi Corporation +# +# Licensed to the Apache Software Foundation (ASF) under one or more contributor +# license agreements. See the NOTICE file distributed with this work for +# additional information regarding copyright ownership. The ASF licenses this +# file to you under the Apache License, Version 2.0 (the "License"); you may not +# use this file except in compliance with the License. You may obtain a copy of +# the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations under +# the License. +# +# ############################################################################## + +if(CONFIG_TESTING_TIMERJITTER) + + set(SRCS timerjitter.c) + + nuttx_add_application( + NAME + timerjitter + PRIORITY + ${CONFIG_TESTING_TIMERJITTER_PRIORITY} + STACKSIZE + ${CONFIG_TESTING_TIMERJITTER_STACKSIZE} + MODULE + ${CONFIG_TESTING_TIMERJITTER} + COMPILE_FLAGS + ${FLAGS} + SRCS + ${SRCS}) + +endif() diff --git a/testing/timerjitter/Kconfig b/testing/timerjitter/Kconfig new file mode 100644 index 000000000..38f1e2ed8 --- /dev/null +++ b/testing/timerjitter/Kconfig @@ -0,0 +1,22 @@ +# +# For a description of the syntax of this configuration file, +# see the file kconfig-language.txt in the NuttX tools repository. +# + +config TESTING_TIMERJITTER + bool "timerjitter testing" + default n + help + timerjitter helps profiling timer accuracy and real-time performance. + +if TESTING_TIMERJITTER + +config TESTING_TIMERJITTER_PRIORITY + int "Priority of timerjitter process" + default 100 + +config TESTING_TIMERJITTER_STACKSIZE + int "Stack size of timerjitter process" + default DEFAULT_TASK_STACKSIZE + +endif diff --git a/testing/timerjitter/Make.defs b/testing/timerjitter/Make.defs new file mode 100644 index 000000000..3d4e9a8f0 --- /dev/null +++ b/testing/timerjitter/Make.defs @@ -0,0 +1,25 @@ +############################################################################ +# apps/testing/timerjitter/Make.defs +# +# Copyright (C) 2024 Xiaomi Corporation +# +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. The +# ASF licenses this file to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance with the +# License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. +# +############################################################################ + +ifneq ($(CONFIG_TESTING_TIMERJITTER),) +CONFIGURED_APPS += $(APPDIR)/testing/timerjitter +endif diff --git a/testing/timerjitter/Makefile b/testing/timerjitter/Makefile new file mode 100644 index 000000000..99290c108 --- /dev/null +++ b/testing/timerjitter/Makefile @@ -0,0 +1,26 @@ +# +# Copyright (C) 2024 Xiaomi Corporation +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +include $(APPDIR)/Make.defs + +PROGNAME = timerjitter +PRIORITY = $(CONFIG_TESTING_TIMERJITTER_PRIORITY) +STACKSIZE = $(CONFIG_TESTING_TIMERJITTER_STACKSIZE) +MODULE = $(CONFIG_TESTING_TIMERJITTER) + +MAINSRC = timerjitter.c + +include $(APPDIR)/Application.mk diff --git a/testing/timerjitter/timerjitter.c b/testing/timerjitter/timerjitter.c new file mode 100644 index 000000000..7972f902c --- /dev/null +++ b/testing/timerjitter/timerjitter.c @@ -0,0 +1,338 @@ +/**************************************************************************** + * apps/testing/timerjitter/timerjitter.c + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. The + * ASF licenses this file to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance with the + * License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include <pthread.h> +#include <stdio.h> +#include <stdlib.h> +#include <signal.h> +#include <time.h> + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +#define DEFAULT_CLOCKID CLOCK_REALTIME +#define DEFAULT_INTERVAL 1000 +#define DEFAULT_ITERATION 1000 + +/* Fix compilation error for Non-NuttX OS */ +#ifndef FAR + #define FAR +#endif + +#ifndef USEC_PER_SEC + #define USEC_PER_SEC 1000000 +#endif + +#ifndef NSEC_PER_SEC + #define NSEC_PER_SEC 1000000000 +#endif + +/**************************************************************************** + * Private Type + ****************************************************************************/ + +struct timerjitter_param_s +{ + clockid_t clockid; + unsigned int interval; + unsigned long max_cnt; + unsigned long cur_cnt; + double avg; + unsigned long max; + unsigned long min; + int print; + unsigned int missed; +}; + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +/* Helper functions for timespec calculating */ + +static inline int64_t calc_diff(FAR const struct timespec *t1, + FAR const struct timespec *t2) +{ + int64_t diff; + + diff = USEC_PER_SEC * (t1->tv_sec - t2->tv_sec); + diff += (t1->tv_nsec - t2->tv_nsec) / 1000; + + return diff; +} + +static inline void ts_norm(FAR struct timespec *ts) +{ + while (ts->tv_nsec >= NSEC_PER_SEC) + { + ts->tv_nsec -= NSEC_PER_SEC; + ts->tv_sec++; + } +} + +static inline int ts_greater(FAR const struct timespec *a, + FAR const struct timespec *b) +{ + return (a->tv_sec > b->tv_sec) || + (a->tv_sec == b->tv_sec && a->tv_nsec > b->tv_nsec); +} + +static inline void calc_next(FAR struct timespec *t, + FAR struct timespec *intv) +{ + t->tv_sec += intv->tv_sec; + t->tv_nsec += intv->tv_nsec; + ts_norm(t); +} + +/* Helper function for number parsing */ + +static unsigned long get_num(FAR const char *str) +{ + FAR char *end; + unsigned long val; + + if (!str) + { + return 0; + } + + val = strtoul(str, &end, 0); + if (!val || val == (unsigned long)-1) + { + return 0; + } + + return val; +} + +static FAR void *timerjitter(FAR void *arg) +{ + FAR struct timerjitter_param_s *param = arg; + struct timespec now; + struct timespec next; + struct timespec intv; + struct itimerspec tspec; + struct sigevent sigev; + sigset_t sigset; + timer_t timer; + int64_t diff; + int sigs; + int ret; + + sigemptyset(&sigset); + sigaddset(&sigset, SIGALRM); + sigprocmask(SIG_BLOCK, &sigset, NULL); + + intv.tv_sec = param->interval / USEC_PER_SEC; + intv.tv_nsec = (param->interval % USEC_PER_SEC) * 1000; + + sigev.sigev_notify = SIGEV_SIGNAL; + sigev.sigev_signo = SIGALRM; + + timer_create(param->clockid, &sigev, &timer); + clock_gettime(param->clockid, &now); + + next = now; + calc_next(&next, &intv); + + /* Set cyclic timer */ + + tspec.it_interval = intv; + + /* Using TIMER_ABSTIME */ + + tspec.it_value = next; + timer_settime(timer, TIMER_ABSTIME, &tspec, NULL); + + param->avg = 0; + param->max = 0; + param->min = (unsigned long)-1; + + while (param->cur_cnt++ < param->max_cnt) + { + /* Wait for SIGALRM */ + + if (sigwait(&sigset, &sigs) < 0) + { + printf("sig wait failed\n"); + break; + } + + ret = clock_gettime(param->clockid, &now); + if (ret) + { + printf("clock_gettime failed %d\n", ret); + } + + diff = calc_diff(&now, &next); + if (param->print) + { + printf("diff %lu, now %lu.%lu\n", diff, now.tv_sec, now.tv_nsec); + } + + if (diff > param->max) + { + param->max = diff; + } + + if (diff < param->min) + { + param->min = diff; + } + + param->avg += diff; + + /* Calculate next = next + intv */ + + calc_next(&next, &intv); + + /* Calibrate if we miss current time frame */ + + while (ts_greater(&now, &next)) + { + calc_next(&next, &intv); + printf("time frame missed %u\n", ++param->missed); + } + } + + param->avg = param->avg / param->cur_cnt; + return NULL; +} + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/**************************************************************************** + * timerjitter main + ****************************************************************************/ + +int main(int argc, FAR char *argv[]) +{ + struct timerjitter_param_s param = + { + .clockid = DEFAULT_CLOCKID, + .interval = DEFAULT_INTERVAL, + .max_cnt = DEFAULT_ITERATION, + .cur_cnt = 0, + .print = 0, + .missed = 0 + }; + + pthread_attr_t attr; + pthread_t thread; + sigset_t sigset; + FAR const char *arg; + int ret; + + /* Mask SIGALRM at first */ + + sigemptyset(&sigset); + sigaddset(&sigset, SIGALRM); + sigprocmask(SIG_BLOCK, &sigset, NULL); + + if (argc > 1) + { + while ((arg = argv[1]) != NULL) + { + if (*arg != '-') + { + break; + } + + for (; ; ) + { + switch (*++arg) + { + case 0: + break; + case 'p': + param.print = 1; + continue; + case 'm': + param.clockid = CLOCK_MONOTONIC; + continue; + case 'r': + param.clockid = CLOCK_REALTIME; + continue; + case 'h': + printf( + "usage: timerjitter [-pmr] [interval(us)] [iteration]\n" + "-p: print time diff between two iteration\n" + "-m: use CLOCK_MONOTONIC\n" + "-r: use CLOCK_REALTIME\n" + ""); + return 0; + default: + printf("Unknown flag '%s'", arg); + return -1; + } + break; + } + + /* Find next parameters */ + + argv++; + argc--; + } + + if (argc > 1) + { + param.interval = get_num(argv[1]); + } + + if (argc > 2) + { + param.max_cnt = get_num(argv[2]); + } + } + + ret = pthread_attr_init(&attr); + if (ret) + { + printf("pthread_attr_init failed %d\n", ret); + } + + ret = pthread_create(&thread, &attr, timerjitter, ¶m); + if (ret) + { + printf("thread created failed %d\n", ret); + } + + pthread_join(thread, NULL); + + ret = pthread_attr_destroy(&attr); + if (ret) + { + printf("pthread_attr_destroy failed %d\n", ret); + } + + printf("timer jitter in %lu run:\n", param.max_cnt); + printf("(latency/us) min: %lu, avg: %.0lf, max %lu\n", + param.min, param.avg, param.max); + + return 0; +}