You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@nuttx.apache.org by GitBox <gi...@apache.org> on 2020/10/08 06:35:12 UTC

[GitHub] [incubator-nuttx-apps] xiaoxiang781216 commented on a change in pull request #421: Add task trace support

xiaoxiang781216 commented on a change in pull request #421:
URL: https://github.com/apache/incubator-nuttx-apps/pull/421#discussion_r500839342



##########
File path: nshlib/nsh_command.c
##########
@@ -518,6 +518,18 @@ static const struct cmdmap_s g_cmdmap[] =
   { "time",     cmd_time,     2, 2, "\"<command>\"" },
 #endif
 
+#ifdef CONFIG_DRIVER_NOTECTL
+# ifndef CONFIG_NSH_DISABLE_TRACE
+  { "trace",    cmd_trace,    1, CONFIG_NSH_MAXARGUMENTS,
+    "[start [<duration>]] [stop] "
+#  ifdef CONFIG_DRIVER_NOTE

Review comment:
       CONFIG_DRIVER_NOTE to CONFIG_DRIVER_NOTERAM?

##########
File path: nshlib/nsh_command.c
##########
@@ -518,6 +518,18 @@ static const struct cmdmap_s g_cmdmap[] =
   { "time",     cmd_time,     2, 2, "\"<command>\"" },
 #endif
 
+#ifdef CONFIG_DRIVER_NOTECTL
+# ifndef CONFIG_NSH_DISABLE_TRACE
+  { "trace",    cmd_trace,    1, CONFIG_NSH_MAXARGUMENTS,

Review comment:
       should we rename all trace to node which is better align with the kernel term?

##########
File path: nshlib/nsh_trace.c
##########
@@ -0,0 +1,1326 @@
+/****************************************************************************
+ * apps/nshlib/nsh_trace.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 <nuttx/config.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <sys/ioctl.h>
+#include <time.h>
+#include <nuttx/clock.h>
+
+#ifdef CONFIG_DRIVER_NOTERAM
+#  include <nuttx/note/noteram_driver.h>
+#endif
+#ifdef CONFIG_DRIVER_NOTECTL
+#  include <nuttx/note/notectl_driver.h>
+#endif
+
+#ifdef CONFIG_SCHED_INSTRUMENTATION_SYSCALL
+#  ifdef CONFIG_LIB_SYSCALL
+#    include <syscall.h>
+#  else
+#    define CONFIG_LIB_SYSCALL
+#    include <syscall.h>
+#    undef CONFIG_LIB_SYSCALL
+#  endif
+#endif
+
+#ifdef CONFIG_SMP
+#  define NCPUS CONFIG_SMP_NCPUS
+#else
+#  define NCPUS 1
+#endif
+
+#include "nsh.h"
+#include "nsh_console.h"
+
+#ifdef CONFIG_DRIVER_NOTECTL
+#  ifndef CONFIG_NSH_DISABLE_TRACE
+
+/****************************************************************************
+ * Pre-processor Definitions
+ ****************************************************************************/
+
+#ifdef CONFIG_DRIVER_NOTERAM
+
+/* Renumber idle task PIDs
+ *  In NuttX, PID number less than NCPUS are idle tasks.
+ *  In Linux, there is only one idle task of PID 0.
+ */
+
+#define get_pid(pid)  ((pid) < NCPUS ? 0 : (pid))
+
+#define get_task_state(s) ((s) <= LAST_READY_TO_RUN_STATE ? 'R' : 'S')
+
+#endif
+
+/****************************************************************************
+ * Private Types
+ ****************************************************************************/
+
+/* The structure to hold the context data of trace dump */
+
+#ifdef CONFIG_DRIVER_NOTERAM
+struct trace_dump_cpu_context_s
+{
+  bool ininterrupt;       /* In interrupt handler flag */
+  bool pendingswitch;     /* sched_switch pending flag */
+  int current_state;      /* Task state of the current line */
+  pid_t current_pid;      /* Task PID of the current line */
+  pid_t next_pid;         /* Task PID of the next line */
+};
+
+struct trace_dump_task_context_s
+{
+  FAR struct trace_dump_task_context_s *next;
+  pid_t pid;                              /* Task PID */
+  int syscall_nest;                       /* Syscall nest level */
+  char name[CONFIG_TASK_NAME_SIZE + 1];   /* Task name (with NUL terminator) */
+};
+#endif
+
+/****************************************************************************
+ * Private Data
+ ****************************************************************************/
+
+static int g_notectlfd;   /* /dev/notectl file descriptor */
+
+#ifdef CONFIG_DRIVER_NOTERAM
+struct trace_dump_cpu_context_s g_dump_cpu_ctx[NCPUS];
+FAR struct trace_dump_task_context_s *g_dump_task_ctx = NULL;
+#endif
+
+/****************************************************************************
+ * Private Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: notectl_enable
+ ****************************************************************************/
+
+static void notectl_enable(int flag)
+{
+  struct note_filter_mode_s mode;
+
+#ifdef CONFIG_BUILD_FLAT
+  sched_note_filter_mode(&mode, NULL);
+#else
+  ioctl(g_notectlfd, NOTECTL_GETMODE, (unsigned long)&mode);
+#endif
+
+  if (flag)
+    {
+      mode.flag |= NOTE_FILTER_MODE_FLAG_ENABLE;
+    }
+  else
+    {
+      mode.flag &= ~NOTE_FILTER_MODE_FLAG_ENABLE;
+    }
+
+#ifdef CONFIG_BUILD_FLAT
+  sched_note_filter_mode(NULL, &mode);
+#else
+  ioctl(g_notectlfd, NOTECTL_SETMODE, (unsigned long)&mode);
+#endif
+}
+
+/****************************************************************************
+ * Name: note_ioctl
+ ****************************************************************************/
+
+#ifdef CONFIG_DRIVER_NOTERAM
+static void note_ioctl(int cmd, unsigned long arg)
+{
+  int fd;
+
+  fd = open("/dev/note", O_RDONLY);
+  if (fd >= 0)
+    {
+      ioctl(fd, cmd, arg);
+      close(fd);
+    }
+}
+#endif
+
+#ifdef CONFIG_DRIVER_NOTERAM
+
+/****************************************************************************
+ * Name: trace_dump_init_context
+ ****************************************************************************/
+
+static void trace_dump_init_context(void)
+{
+  int cpu;
+
+  /* Initialize the trace dump context */
+
+  for (cpu = 0; cpu < NCPUS; cpu++)
+    {
+      g_dump_cpu_ctx[cpu].ininterrupt = false;
+      g_dump_cpu_ctx[cpu].pendingswitch = false;
+      g_dump_cpu_ctx[cpu].current_state = TSTATE_TASK_RUNNING;
+      g_dump_cpu_ctx[cpu].current_pid = 0;
+      g_dump_cpu_ctx[cpu].next_pid = 0;
+    }
+}
+
+/****************************************************************************
+ * Name: trace_dump_fini_context
+ ****************************************************************************/
+
+static void trace_dump_fini_context(void)
+{
+  FAR struct trace_dump_task_context_s *tctx;
+  FAR struct trace_dump_task_context_s *ntctx;
+
+  /* Finalize the trace dump context */
+
+  tctx = g_dump_task_ctx;
+  g_dump_task_ctx = NULL;
+  while (tctx != NULL)
+    {
+      ntctx = tctx->next;
+      free(tctx);
+      tctx = ntctx;
+    }
+}
+
+/****************************************************************************
+ * Name: copy_task_name
+ ****************************************************************************/
+
+#if CONFIG_TASK_NAME_SIZE > 0
+static void copy_task_name(FAR char *dst, FAR const char *src)
+{
+  char c;
+  int i;
+
+  /* Replace space to underline
+   * Text trace data format cannot use a space as a task name.
+   */
+
+  for (i = 0; i < CONFIG_TASK_NAME_SIZE; i++)
+    {
+      c = *src++;
+      if (c == '\0')
+        {
+          break;
+        }
+
+      *dst++ = (c == ' ') ? '_' : c;
+    }
+
+  *dst = '\0';
+}
+#endif
+
+/****************************************************************************
+ * Name: get_task_context
+ ****************************************************************************/
+
+FAR static struct trace_dump_task_context_s *get_task_context(pid_t pid)
+{
+  FAR struct trace_dump_task_context_s **tctxp;
+  tctxp = &g_dump_task_ctx;
+  while (*tctxp != NULL)
+    {
+      if ((*tctxp)->pid == pid)
+        {
+          return *tctxp;
+        }
+
+      tctxp = &((*tctxp)->next);
+    }
+
+  /* Create new trace dump task context */
+
+  *tctxp = (FAR struct trace_dump_task_context_s *)
+           malloc(sizeof(struct trace_dump_task_context_s));
+  if (*tctxp != NULL)
+    {
+      (*tctxp)->next = NULL;
+      (*tctxp)->pid = pid;
+      (*tctxp)->syscall_nest = 0;
+      (*tctxp)->name[0] = '\0';
+    }
+
+  return *tctxp;
+}
+
+/****************************************************************************
+ * Name: get_task_name
+ ****************************************************************************/
+
+static const char *get_task_name(pid_t pid)
+{
+  FAR struct trace_dump_task_context_s *tctxp;
+
+  tctxp = get_task_context(pid);
+  if (tctxp != NULL && tctxp->name[0] != '\0')
+    {
+      return tctxp->name;
+    }
+
+  return "<noname>";
+}
+
+/****************************************************************************
+ * Name: trace_dump_header
+ ****************************************************************************/
+
+static void trace_dump_header(FILE *out,
+                              FAR struct note_common_s *note)
+{
+  pid_t pid;
+  uint32_t systime = note->nc_systime[0] +
+                     (note->nc_systime[1] << 8) +
+                     (note->nc_systime[2] << 16) +
+                     (note->nc_systime[3] << 24);
+#ifdef CONFIG_SMP
+  int cpu = note->nc_cpu;
+#else
+  int cpu = 0;
+#endif
+
+  pid = g_dump_cpu_ctx[cpu].current_pid;
+
+  fprintf(out, "%8s-%-3u [%d] %3u.%09u: ",
+          get_task_name(pid), get_pid(pid), cpu,
+          systime / (1000 * 1000 / CONFIG_USEC_PER_TICK),
+          (systime % (1000 * 1000 / CONFIG_USEC_PER_TICK))
+            * CONFIG_USEC_PER_TICK * 1000);
+}
+
+/****************************************************************************
+ * Name: trace_dump_sched_switch
+ ****************************************************************************/
+
+static void trace_dump_sched_switch(FILE *out,
+                                    FAR struct note_common_s *note)
+{
+  FAR struct trace_dump_cpu_context_s *ctxp;
+  pid_t current_pid;
+  pid_t next_pid;
+#ifdef CONFIG_SMP
+  int cpu = note->nc_cpu;
+#else
+  int cpu = 0;
+#endif
+
+  ctxp = &g_dump_cpu_ctx[cpu];
+  current_pid = ctxp->current_pid;
+  next_pid = ctxp->next_pid;
+
+  fprintf(out, "sched_switch: "
+               "prev_comm=%s prev_pid=%u prev_state=%c ==> "
+               "next_comm=%s next_pid=%u\n",
+          get_task_name(current_pid), get_pid(current_pid),
+          get_task_state(ctxp->current_state),
+          get_task_name(next_pid), get_pid(next_pid));
+
+  ctxp->current_pid = ctxp->next_pid;
+  ctxp->pendingswitch = false;
+}
+
+/****************************************************************************
+ * Name: trace_dump_one
+ ****************************************************************************/
+
+static int trace_dump_one(FILE *out,
+                          FAR uint8_t *p)
+{
+  FAR struct note_common_s *note = (FAR struct note_common_s *)p;
+  FAR struct trace_dump_cpu_context_s *ctxp;
+  pid_t pid;
+#ifdef CONFIG_SMP
+  int cpu = note->nc_cpu;
+#else
+  int cpu = 0;
+#endif
+
+  ctxp = &g_dump_cpu_ctx[cpu];
+  pid = note->nc_pid[0] + (note->nc_pid[1] << 8);
+
+  if (note->nc_type != NOTE_START &&
+      note->nc_type != NOTE_STOP &&
+      note->nc_type != NOTE_RESUME
+#ifdef CONFIG_SMP
+      && !(note->nc_type >= NOTE_CPU_START &&
+           note->nc_type <= NOTE_CPU_RESUMED)
+#endif
+     )
+    {
+      ctxp->current_pid = pid;
+    }
+
+  /* Output one note */
+
+  switch (note->nc_type)
+    {
+      case NOTE_START:
+        {
+#if CONFIG_TASK_NAME_SIZE > 0
+          FAR struct note_start_s *nst = (FAR struct note_start_s *)p;
+          FAR struct trace_dump_task_context_s *tctxp;
+
+          tctxp = get_task_context(pid);
+          if (tctxp != NULL)
+            {
+              copy_task_name(tctxp->name, nst->nst_name);
+            }
+#endif
+
+          trace_dump_header(out, note);
+          fprintf(out, "sched_wakeup_new: comm=%s pid=%d target_cpu=%d\n",
+                  get_task_name(pid), get_pid(pid), cpu);
+        }
+        break;
+
+      case NOTE_STOP:
+        {
+          trace_dump_header(out, note);
+          fprintf(out, "sched_switch: "
+                       "prev_comm=%s prev_pid=%u prev_state=%c ==> "
+                       "next_comm=%s next_pid=%u\n",
+                  get_task_name(pid), get_pid(pid), 'X',
+                  get_task_name(ctxp->current_pid),
+                  get_pid(ctxp->current_pid));
+        }
+        break;
+
+      case NOTE_SUSPEND:
+        {
+          FAR struct note_suspend_s *nsu = (FAR struct note_suspend_s *)p;
+
+          /* This note informs the task to be suspended.
+           * Preserve the information for the succeeding NOTE_RESUME.
+           */
+
+          ctxp->current_state = nsu->nsu_state;
+        }
+        break;
+
+      case NOTE_RESUME:
+        {
+          /* This note informs the task to be resumed.
+           * The task switch timing depends on the running context.
+           */
+
+          ctxp->next_pid = pid;
+
+          if (!ctxp->ininterrupt)
+            {
+              /* If not in the interrupt context, the task switch is
+               * executed immediately.
+               */
+
+              trace_dump_header(out, note);
+              trace_dump_sched_switch(out, note);
+            }
+          else
+            {
+              /* If in the interrupt context, the task switch is postponed
+               * until leaving the interrupt handler.
+               */
+
+              trace_dump_header(out, note);
+              fprintf(out, "sched_waking: comm=%s pid=%d target_cpu=%d\n",
+                      get_task_name(ctxp->next_pid),
+                      get_pid(ctxp->next_pid), cpu);
+              ctxp->pendingswitch = true;
+            }
+        }
+        break;
+
+#ifdef CONFIG_SCHED_INSTRUMENTATION_SYSCALL
+      case NOTE_SYSCALL_ENTER:
+        {
+          FAR struct note_syscall_enter_s *nsc;
+          FAR struct trace_dump_task_context_s *tctxp;
+
+          /* Exclude the case of syscall issued by an interrupt handler and
+           * nested syscalls to correct tracecompass display.
+           */
+
+          if (ctxp->ininterrupt)
+            {
+              break;
+            }
+
+          tctxp = get_task_context(pid);
+          if (tctxp == NULL)
+            {
+              break;
+            }
+
+          tctxp->syscall_nest++;
+          if (tctxp->syscall_nest > 1)
+            {
+              break;
+            }
+
+          nsc = (FAR struct note_syscall_enter_s *)p;
+          trace_dump_header(out, note);
+          fprintf(out, "sys_%s()\n",
+                  g_funcnames[nsc->nsc_nr - CONFIG_SYS_RESERVED]);
+        }
+        break;
+
+      case NOTE_SYSCALL_LEAVE:
+        {
+          FAR struct note_syscall_leave_s *nsc;
+          FAR struct trace_dump_task_context_s *tctxp;
+
+          /* Exclude the case of syscall issued by an interrupt handler and
+           * nested syscalls to correct tracecompass display.
+           */
+
+          if (ctxp->ininterrupt)
+            {
+              break;
+            }
+
+          tctxp = get_task_context(pid);
+          if (tctxp == NULL)
+            {
+              break;
+            }
+
+          tctxp->syscall_nest--;
+          if (tctxp->syscall_nest > 0)
+            {
+              break;
+            }
+
+          tctxp->syscall_nest = 0;
+
+          nsc = (FAR struct note_syscall_leave_s *)p;
+          trace_dump_header(out, note);
+          fprintf(out, "sys_%s -> 0x%x\n",
+                  g_funcnames[nsc->nsc_nr - CONFIG_SYS_RESERVED],
+                  nsc->nsc_result);
+        }
+        break;
+#endif
+
+#ifdef CONFIG_SCHED_INSTRUMENTATION_IRQHANDLER
+      case NOTE_IRQ_ENTER:
+        {
+          FAR struct note_irqhandler_s *nih;
+
+          nih = (FAR struct note_irqhandler_s *)p;
+          trace_dump_header(out, note);
+          fprintf(out, "irq_handler_entry: irq=%u\n",
+                  nih->nih_irq);
+          ctxp->ininterrupt = true;
+        }
+        break;
+
+      case NOTE_IRQ_LEAVE:
+        {
+          FAR struct note_irqhandler_s *nih;
+
+          nih = (FAR struct note_irqhandler_s *)p;
+          trace_dump_header(out, note);
+          fprintf(out, "irq_handler_exit: irq=%u\n",
+                  nih->nih_irq);
+          ctxp->ininterrupt = false;
+          if (ctxp->pendingswitch)
+            {
+              /* If the pending task switch exists, it is executed here */
+
+              trace_dump_header(out, note);
+              trace_dump_sched_switch(out, note);
+            }
+        }
+        break;
+#endif
+
+      default:
+        break;
+    }
+
+  /* Return the length of the processed note */
+
+  return note->nc_length;
+}
+
+/****************************************************************************
+ * Name: trace_dump
+ ****************************************************************************/
+
+static int trace_dump(FILE *out)
+{
+  uint8_t tracedata[UCHAR_MAX];
+  FAR uint8_t *p;
+  int size;
+  int ret;
+  int fd;
+
+  /* Stop the tracing before dump */
+
+  notectl_enable(false);
+
+  /* Open note for read */
+
+  fd = open("/dev/note", O_RDONLY);
+  if (fd < 0)
+    {
+      return ERROR;
+    }
+
+  trace_dump_init_context();
+
+  /* Read and output all notes */
+
+  while (1)
+    {
+      ret = read(fd, tracedata, sizeof tracedata);
+      if (ret <= 0)
+        {
+          break;
+        }
+
+      p = tracedata;
+      do
+        {
+          size = trace_dump_one(out, p);
+          p += size;
+          ret -= size;
+        }
+      while (ret > 0);
+    }
+
+  trace_dump_fini_context();
+
+  /* Close note */
+
+  close(fd);
+
+  return ret;
+}
+
+#endif /* CONFIG_DRIVER_NOTERAM */
+
+/****************************************************************************
+ * Name: trace_cmd_start
+ ****************************************************************************/
+
+static int trace_cmd_start(FAR struct nsh_vtbl_s *vtbl,
+                           int index, int argc, char **argv)
+{
+  char *endptr;
+  int duration = 0;
+
+  /* Usage: trace start [<duration>] */
+
+  if (index < argc - 1)
+    {
+      index++;
+      duration = strtoul(argv[index], &endptr, 0);
+      if (!duration || endptr == argv[index] || *endptr != '\0')
+        {
+          nsh_output(vtbl, g_fmtarginvalid, argv[0]);
+          return ERROR;
+        }
+    }
+
+  /* Clear the trace buffer */
+
+#ifdef CONFIG_DRIVER_NOTERAM
+  note_ioctl(NOTERAM_CLEAR, 0);
+#endif
+
+  /* Start tracing */
+
+  notectl_enable(true);
+
+  if (duration > 0)
+    {
+      /* If <duration> is given, stop tracing after specified seconds. */
+
+      sleep(duration);
+      notectl_enable(false);
+    }
+
+  return index;
+}
+
+/****************************************************************************
+ * Name: trace_cmd_dump
+ ****************************************************************************/
+
+#ifdef CONFIG_DRIVER_NOTERAM
+static int trace_cmd_dump(FAR struct nsh_vtbl_s *vtbl,
+                          int index, int argc, char **argv)
+{
+  FAR char *fullpath;
+  FILE *out = stdout;
+  int ret;
+
+  /* Usage: trace dump [<filename>] */
+
+  /* If <filename> is '-' or not given, trace dump is displayed
+   * to stdout.
+   */
+
+  if (index < argc - 1)
+    {
+      index++;
+      if (strcmp(argv[index], "-") != 0)
+        {
+          /* If <filename> is given, open the file stream for output. */
+
+          fullpath = nsh_getfullpath(vtbl, argv[index]);
+          if (fullpath == NULL)
+            {
+              nsh_output(vtbl, g_fmtcmdoutofmemory, argv[0]);
+              return ERROR;
+            }
+
+          out = fopen(fullpath, "w");
+          if (out == NULL)
+            {
+              nsh_output(vtbl, g_fmtcmdfailed, argv[0], "open", NSH_ERRNO);
+              return ERROR;
+            }
+
+          nsh_freefullpath(fullpath);
+        }
+    }
+
+  /* Dump the trace data */
+
+  ret = trace_dump(out);
+
+  /* If needed, close the file stream for dump. */
+
+  if (out != stdout)
+    {
+      fclose(out);
+    }
+
+  if (ret < 0)
+    {
+      nsh_output(vtbl, g_fmtcmdfailed, argv[0], "open", NSH_ERRNO);
+      return ERROR;
+    }
+
+  return index;
+}
+#endif
+
+/****************************************************************************
+ * Name: trace_cmd_cmd
+ ****************************************************************************/
+
+static int trace_cmd_cmd(FAR struct nsh_vtbl_s *vtbl,
+                         int index, int argc, char **argv)
+{
+#ifndef CONFIG_NSH_DISABLEBG
+  bool bgsave;
+#endif
+#if CONFIG_FILE_STREAM
+  bool redirsave;
+#endif
+  int ret;
+
+  /* Usage: trace cmd "<command>" */
+
+  index++;
+  if (index >= argc)
+    {
+      /* <command> parameter is mandatory. */
+
+      nsh_output(vtbl, g_fmtargrequired, argv[0]);
+      return ERROR;
+    }
+
+  /* Save state */
+
+#ifndef CONFIG_NSH_DISABLEBG
+  bgsave    = vtbl->np.np_bg;
+#endif
+#if CONFIG_FILE_STREAM
+  redirsave = vtbl->np.np_redirect;
+#endif
+
+  /* Clear the trace buffer */
+
+#ifdef CONFIG_DRIVER_NOTERAM
+  note_ioctl(NOTERAM_CLEAR, 0);
+#endif
+
+  /* Execute the command with tracing */
+
+  notectl_enable(true);
+  ret = nsh_parse(vtbl, argv[index]);
+  notectl_enable(false);
+
+  /* Restore state */
+
+#ifndef CONFIG_NSH_DISABLEBG
+  vtbl->np.np_bg       = bgsave;
+#endif
+#if CONFIG_FILE_STREAM
+  vtbl->np.np_redirect = redirsave;
+#endif
+
+  if (ret < 0)
+    {
+      return ret;
+    }
+
+  return index;
+}
+
+/****************************************************************************
+ * Name: trace_cmd_mode
+ ****************************************************************************/
+
+static int trace_cmd_mode(FAR struct nsh_vtbl_s *vtbl,
+                          int index, int argc, char **argv)
+{
+  struct note_filter_mode_s mode;
+#ifdef CONFIG_DRIVER_NOTERAM
+  unsigned int owmode = 0;
+#endif
+  bool enable;
+  bool modified;
+#ifdef CONFIG_SCHED_INSTRUMENTATION_SYSCALL
+  struct note_filter_syscall_s filter_syscall;
+#endif
+#ifdef CONFIG_SCHED_INSTRUMENTATION_IRQHANDLER
+  struct note_filter_irq_s filter_irq;
+#endif
+  int i;
+  int count;
+
+  /* Usage: trace mode [{+|-}{o|s|i}...] */
+
+  /* Get current trace mode */
+
+  ioctl(g_notectlfd, NOTECTL_GETMODE, (unsigned long)&mode);
+#ifdef CONFIG_DRIVER_NOTERAM
+  note_ioctl(NOTERAM_GETMODE, (unsigned long)&owmode);
+#endif
+
+  modified = false;
+
+  /* Parse the mode setting parameters */
+
+  while (index < argc - 1)
+    {
+      if (argv[index + 1][0] != '-' && argv[index + 1][0] != '+')
+        {
+          break;
+        }
+
+      index++;
+      enable = (argv[index][0] == '+');
+
+      switch (argv[index][1])
+        {
+#ifdef CONFIG_DRIVER_NOTERAM
+          case 'o':   /* Overwrite mode */
+            if (enable)
+              {
+                owmode = NOTERAM_MODE_OVERWRITE_ENABLE;
+              }
+            else
+              {
+                owmode = NOTERAM_MODE_OVERWRITE_DISABLE;
+              }
+            break;
+#endif
+
+#ifdef CONFIG_SCHED_INSTRUMENTATION_SYSCALL
+          case 's':   /* Syscall trace */
+            if (enable)
+              {
+                mode.flag |= NOTE_FILTER_MODE_FLAG_SYSCALL;
+              }
+            else
+              {
+                mode.flag &= ~NOTE_FILTER_MODE_FLAG_SYSCALL;
+              }
+            break;
+#endif
+
+#ifdef CONFIG_SCHED_INSTRUMENTATION_IRQHANDLER
+          case 'i':   /* IRQ trace */
+            if (enable)
+              {
+                mode.flag |= NOTE_FILTER_MODE_FLAG_IRQ;
+              }
+            else
+              {
+                mode.flag &= ~NOTE_FILTER_MODE_FLAG_IRQ;
+              }
+            break;
+#endif
+
+          default:
+            nsh_output(vtbl, g_fmtsyntax, argv[0]);
+            return ERROR;
+        }
+
+      modified = true;
+    }
+
+  if (modified)
+    {
+      /* Update trace mode */
+
+      ioctl(g_notectlfd, NOTECTL_SETMODE, (unsigned long)&mode);
+#ifdef CONFIG_DRIVER_NOTERAM
+      note_ioctl(NOTERAM_SETMODE, (unsigned long)&owmode);
+#endif
+
+      return index;
+    }
+
+  /* If no parameter, display current trace mode setting. */
+
+  nsh_output(vtbl, "Task trace mode:\n");
+  nsh_output(vtbl, " Trace                   : %s\n",
+             (mode.flag & NOTE_FILTER_MODE_FLAG_ENABLE) ?
+              "enabled" : "disabled");
+
+#ifdef CONFIG_DRIVER_NOTERAM
+  nsh_output(vtbl, " Overwrite               : %s\n",
+             (owmode == NOTERAM_MODE_OVERWRITE_ENABLE) ?
+              "on  (+o)" : "off (-o)");
+#endif
+
+#ifdef CONFIG_SCHED_INSTRUMENTATION_SYSCALL
+  ioctl(g_notectlfd, NOTECTL_GETSYSCALLFILTER,
+        (unsigned long)&filter_syscall);
+  count = 0;
+  for (i = 0; i < SYS_nsyscalls; i++)
+    {
+      if (NOTE_FILTER_SYSCALLMASK_ISSET(i, &filter_syscall))
+        {
+          count++;
+        }
+    }
+
+  nsh_output(vtbl, " Syscall trace           : %s\n",
+             mode.flag & NOTE_FILTER_MODE_FLAG_SYSCALL ?
+               "on  (+s)" : "off (-s)");
+  if (mode.flag & NOTE_FILTER_MODE_FLAG_SYSCALL)
+    {
+      nsh_output(vtbl, "  Filtered Syscalls      : %d\n", count);
+    }
+#endif
+
+#ifdef CONFIG_SCHED_INSTRUMENTATION_IRQHANDLER
+  ioctl(g_notectlfd, NOTECTL_GETIRQFILTER, (unsigned long)&filter_irq);
+  count = 0;
+  for (i = 0; i < NR_IRQS; i++)
+    {
+      if (NOTE_FILTER_IRQMASK_ISSET(i, &filter_irq))
+        {
+          count++;
+        }
+    }
+
+  nsh_output(vtbl, " IRQ trace               : %s\n",
+             mode.flag & NOTE_FILTER_MODE_FLAG_IRQ ?
+               "on  (+i)" : "off (-i)");
+  if (mode.flag & NOTE_FILTER_MODE_FLAG_IRQ)
+    {
+      nsh_output(vtbl, "  Filtered IRQs          : %d\n", count);
+    }
+#endif
+
+  return index;
+}
+
+/****************************************************************************
+ * Name: match_syscall
+ ****************************************************************************/
+
+#ifdef CONFIG_SCHED_INSTRUMENTATION_SYSCALL
+static int match_syscall(FAR const char *pattern, FAR const char *name)
+{
+  FAR const char *p = pattern;
+  FAR const char *n = name;
+
+  /* Simple wildcard matcher to specify the syscalls to be masked */
+
+  while (1)
+    {
+      if (*n == '\0')
+        {
+          if (*p == '*')
+            {
+              p++;
+            }
+
+          return *p == '\0';
+        }
+      else if (*p == '\0')
+        {
+          return false;
+        }
+      else if (*p == '*')
+        {
+          if (p[1] == '\0')
+            {
+              return true;
+            }
+          else if (match_syscall(p, n + 1))
+            {
+              return true;
+            }
+          else if (match_syscall(p + 1, n))
+            {
+              return true;
+            }
+
+          return false;
+        }
+      else if (*p == *n)
+        {
+          p++;
+          n++;
+        }
+      else
+        {
+          return false;
+        }
+    }
+}
+#endif
+
+/****************************************************************************
+ * Name: trace_cmd_syscall
+ ****************************************************************************/
+
+#ifdef CONFIG_SCHED_INSTRUMENTATION_SYSCALL
+static int trace_cmd_syscall(FAR struct nsh_vtbl_s *vtbl,
+                            int index, int argc, char **argv)
+{
+  struct note_filter_mode_s mode;
+  bool enable;
+  bool modified = false;
+  int syscallno;
+  FAR struct note_filter_syscall_s filter_syscall;
+  int n;
+  int count;
+
+  /* Usage: trace syscall [{+|-}<syscallname>...] */
+
+  /* Get current syscall filter setting */
+
+  ioctl(g_notectlfd, NOTECTL_GETSYSCALLFILTER,
+        (unsigned long)&filter_syscall);
+
+  /* Parse the setting parameters */
+
+  while (index < argc - 1)
+    {
+      if (argv[index + 1][0] != '-' && argv[index + 1][0] != '+')
+        {
+          break;
+        }
+
+      index++;
+      modified = true;
+      enable = (argv[index][0] == '+');
+
+      /* Check whether the given pattern matches for each syscall names */
+
+      for (syscallno = 0; syscallno < SYS_nsyscalls; syscallno++)
+        {
+          if (!match_syscall(&argv[index][1], g_funcnames[syscallno]))
+            {
+              continue;
+            }
+
+          /* If matches, update the masked syscall number list */
+
+          if (enable)
+            {
+              NOTE_FILTER_SYSCALLMASK_SET(syscallno, &filter_syscall);
+            }
+          else
+            {
+              NOTE_FILTER_SYSCALLMASK_CLR(syscallno, &filter_syscall);
+            }
+        }
+    }
+
+  if (modified)
+    {
+      /* Update current syscall filter setting */
+
+      ioctl(g_notectlfd, NOTECTL_SETSYSCALLFILTER,
+            (unsigned long)&filter_syscall);
+
+      /* Enable syscall trace flag */
+
+      ioctl(g_notectlfd, NOTECTL_GETMODE, (unsigned long)&mode);
+      mode.flag |= NOTE_FILTER_MODE_FLAG_SYSCALL;
+      ioctl(g_notectlfd, NOTECTL_SETMODE, (unsigned long)&mode);
+    }
+  else
+    {
+      /* If no parameter, display current setting. */
+
+      count = 0;
+      for (n = 0; n < SYS_nsyscalls; n++)
+        {
+          if (NOTE_FILTER_SYSCALLMASK_ISSET(n, &filter_syscall))
+            {
+              count++;
+            }
+        }
+
+      nsh_output(vtbl, "Filtered Syscalls: %d\n", count);
+
+      for (n = 0; n < SYS_nsyscalls; n++)
+        {
+          if (NOTE_FILTER_SYSCALLMASK_ISSET(n, &filter_syscall))
+            {
+              nsh_output(vtbl, "  %s\n", g_funcnames[n]);
+            }
+        }
+    }
+
+  return index;
+}
+#endif
+
+/****************************************************************************
+ * Name: trace_cmd_irq
+ ****************************************************************************/
+
+#ifdef CONFIG_SCHED_INSTRUMENTATION_IRQHANDLER
+static int trace_cmd_irq(FAR struct nsh_vtbl_s *vtbl,
+                         int index, int argc, char **argv)
+{
+  struct note_filter_mode_s mode;
+  bool enable;
+  bool modified = false;
+  int irqno;
+  char *endptr;
+  struct note_filter_irq_s filter_irq;
+  int n;
+  int count;
+
+  /* Usage: trace irq [{+|-}<irqnum>...] */
+
+  /* Get current irq filter setting */
+
+  ioctl(g_notectlfd, NOTECTL_GETIRQFILTER, (unsigned long)&filter_irq);
+
+  /* Parse the setting parameters */
+
+  while (index < argc - 1)
+    {
+      if (argv[index + 1][0] != '-' && argv[index + 1][0] != '+')
+        {
+          break;
+        }
+
+      index++;
+      modified = true;
+      enable = (argv[index][0] == '+');
+
+      if (argv[index][1] == '*')
+        {
+          /* Mask or unmask all IRQs */
+
+          if (enable)
+            {
+              for (n = 0; n < NR_IRQS; n++)
+                {
+                  NOTE_FILTER_IRQMASK_SET(n, &filter_irq);
+                }
+            }
+          else
+            {
+              NOTE_FILTER_IRQMASK_ZERO(&filter_irq);
+            }
+
+          continue;
+        }
+
+      /* Get IRQ number */
+
+      irqno = strtoul(&argv[index][1], &endptr, 0);
+      if (endptr == &argv[index][1] || *endptr != '\0' ||
+          irqno >= NR_IRQS)
+        {
+          nsh_output(vtbl, g_fmtarginvalid, argv[0]);
+          return ERROR;
+        }
+
+      /* Update the masked IRQ number list */
+
+      if (enable)
+        {
+          NOTE_FILTER_IRQMASK_SET(irqno, &filter_irq);
+        }
+      else
+        {
+          NOTE_FILTER_IRQMASK_CLR(irqno, &filter_irq);
+        }
+    }
+
+  if (modified)
+    {
+      /* Update current irq filter setting */
+
+      ioctl(g_notectlfd, NOTECTL_SETIRQFILTER, (unsigned long)&filter_irq);
+
+      /* Enable irq trace flag */
+
+      ioctl(g_notectlfd, NOTECTL_GETMODE, (unsigned long)&mode);
+      mode.flag |= NOTE_FILTER_MODE_FLAG_IRQ;
+      ioctl(g_notectlfd, NOTECTL_SETMODE, (unsigned long)&mode);
+    }
+  else
+    {
+      /* If no parameter, display current setting. */
+
+      count = 0;
+      for (n = 0; n < NR_IRQS; n++)
+        {
+          if (NOTE_FILTER_IRQMASK_ISSET(n, &filter_irq))
+            {
+              count++;
+            }
+        }
+
+      nsh_output(vtbl, "Filtered IRQs: %d\n", count);
+
+      for (n = 0; n < NR_IRQS; n++)
+        {
+          if (NOTE_FILTER_IRQMASK_ISSET(n, &filter_irq))
+            {
+              nsh_output(vtbl, "  %d\n", n);
+            }
+        }
+    }
+
+  return index;
+}
+#endif
+
+/****************************************************************************
+ * Public Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: cmd_trace
+ ****************************************************************************/
+
+int cmd_trace(FAR struct nsh_vtbl_s *vtbl, int argc, char **argv)
+{
+  int ret = OK;
+  int i;
+
+  /* Open note control device */
+
+  g_notectlfd = open("/dev/notectl", 0);

Review comment:
       change g_notectlfd  to local variable and pass it to other function as argument?

##########
File path: nshlib/nsh_trace.c
##########
@@ -0,0 +1,1326 @@
+/****************************************************************************
+ * apps/nshlib/nsh_trace.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 <nuttx/config.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <sys/ioctl.h>
+#include <time.h>
+#include <nuttx/clock.h>
+
+#ifdef CONFIG_DRIVER_NOTERAM
+#  include <nuttx/note/noteram_driver.h>
+#endif
+#ifdef CONFIG_DRIVER_NOTECTL
+#  include <nuttx/note/notectl_driver.h>
+#endif
+
+#ifdef CONFIG_SCHED_INSTRUMENTATION_SYSCALL
+#  ifdef CONFIG_LIB_SYSCALL
+#    include <syscall.h>
+#  else
+#    define CONFIG_LIB_SYSCALL
+#    include <syscall.h>
+#    undef CONFIG_LIB_SYSCALL
+#  endif
+#endif
+
+#ifdef CONFIG_SMP
+#  define NCPUS CONFIG_SMP_NCPUS
+#else
+#  define NCPUS 1
+#endif
+
+#include "nsh.h"
+#include "nsh_console.h"
+
+#ifdef CONFIG_DRIVER_NOTECTL

Review comment:
       don't need? guard by Makefile already.




----------------------------------------------------------------
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

For queries about this service, please contact Infrastructure at:
users@infra.apache.org