text: /*
   * Copyright (c) 2018 Nordic Semiconductor ASA
   *
   * SPDX-License-Identifier: Apache-2.0
   */
  
  #include <zephyr.h>
  #include <string.h>
  #include <sys/printk.h>
  #include "sample_instance.h"
  #include "sample_module.h"
  #include "ext_log_system.h"
  #include "ext_log_system_adapter.h"
  #include <logging/log_ctrl.h>
  #include <app_memory/app_memdomain.h>
  
  #include <logging/log.h>
  LOG_MODULE_REGISTER(main);
  
  #ifdef CONFIG_USERSPACE
  K_APPMEM_PARTITION_DEFINE(app_part);
  static struct k_mem_domain app_domain;
  static struct k_mem_partition *app_parts[] = {
  #ifdef Z_LIBC_PARTITION_EXISTS
  		/* C library globals, stack canary storage, etc */
  		&z_libc_partition,
  #endif
  		&app_part
  };
  #endif /* CONFIG_USERSPACE */
  
  /* size of stack area used by each thread */
  #define STACKSIZE (1024 + CONFIG_TEST_EXTRA_STACKSIZE)
  
  extern void sample_module_func(void);
  
  #define INST1_NAME Z_STRINGIFY(SAMPLE_INSTANCE_NAME.inst1)
  SAMPLE_INSTANCE_DEFINE(app_part, inst1);
  
  #define INST2_NAME Z_STRINGIFY(SAMPLE_INSTANCE_NAME.inst2)
  SAMPLE_INSTANCE_DEFINE(app_part, inst2);
  
  #if !defined(NRF_RTC1) && defined(CONFIG_SOC_FAMILY_NRF)
  #include <soc.h>
  #endif
  
  static uint32_t timestamp_get(void)
  {
  #ifdef CONFIG_SOC_FAMILY_NRF
  	return NRF_RTC1->COUNTER;
  #else
  	return k_cycle_get_32();
  #endif
  }
  
  static uint32_t timestamp_freq(void)
  {
  #ifdef CONFIG_SOC_FAMILY_NRF
  	return 32768 / (NRF_RTC1->PRESCALER + 1);
  #else
  	return sys_clock_hw_cycles_per_sec();
  #endif
  }
  
  /**
   * @brief Function for finding source ID based on source name.
   *
   * @param name Source name
   *
   * @return Source ID.
   */
  static int16_t log_source_id_get(const char *name)
  {
  
  	for (int16_t i = 0; i < log_src_cnt_get(CONFIG_LOG_DOMAIN_ID); i++) {
  		if (strcmp(log_source_name_get(CONFIG_LOG_DOMAIN_ID, i), name)
  		    == 0) {
  			return i;
  		}
  	}
  
  	return -1;
  }
  
  /**
   * @brief Function demonstrates module level filtering.
   *
   * Sample module API is called then logging for this module is disabled and
   * function is called again. It is expected that only logs generated by the
   * first call will be processed by the output.
   */
  static void module_logging_showcase(void)
  {
  	printk("Module logging showcase.\n");
  
  	sample_module_func();
  	inline_func();
  
  	if (IS_ENABLED(CONFIG_LOG_RUNTIME_FILTERING)) {
  		printk("Disabling logging in the %s module\n",
  					sample_module_name_get());
  
  		log_filter_set(NULL, 0,
  			       log_source_id_get(sample_module_name_get()),
  			       LOG_LEVEL_NONE);
  
  		sample_module_func();
  
  		printk("Function called again but with logging disabled.\n");
  
  	} else {
  		printk("%s option disabled.\n",
  		       Z_STRINGIFY(CONFIG_LOG_RUNTIME_FILTERING));
  	}
  }
  
  /**
   * @brief Function demonstrates instance level filtering.
   *
   * Sample multi-instance module API on two instances is called then logging
   * level for one instance is reduced and function is called again on two
   * instances. It is expected that one instance will generate less logs.
   */
  static void instance_logging_showcase(void)
  {
  	printk("Instance level logging showcase.\n");
  
  	sample_instance_inline_call(&inst1);
  	sample_instance_call(&inst1);
  	sample_instance_inline_call(&inst2);
  	sample_instance_call(&inst2);
  
  	if (IS_ENABLED(CONFIG_LOG_RUNTIME_FILTERING)) {
  		printk("Changing filter to warning on %s instance.\n",
  								INST1_NAME);
  
  		log_filter_set(NULL, 0,
  			       log_source_id_get(INST1_NAME), LOG_LEVEL_WRN);
  
  		sample_instance_inline_call(&inst1);
  		sample_instance_call(&inst1);
  		sample_instance_inline_call(&inst2);
  		sample_instance_call(&inst2);
  
  		printk("Disabling logging on both instances.\n");
  
  		log_filter_set(NULL, 0,
  			       log_source_id_get(INST1_NAME),
  			       LOG_LEVEL_NONE);
  
  		log_filter_set(NULL, 0,
  			       log_source_id_get(INST2_NAME),
  			       LOG_LEVEL_NONE);
  
  		sample_instance_inline_call(&inst1);
  		sample_instance_call(&inst1);
  		sample_instance_inline_call(&inst2);
  		sample_instance_call(&inst2);
  
  		printk("Function call on both instances with logging disabled.\n");
  	}
  }
  
  /**
   * @brief Function demonstrates supported severity logging level.
   */
  static void severity_levels_showcase(void)
  {
  	printk("Severity levels showcase.\n");
  
  	LOG_ERR("Error message example.");
  	LOG_WRN("Warning message example.");
  	LOG_INF("Info message example.");
  	LOG_DBG("Debug message example.");
  }
  
  /**
   * @brief Function demonstrates how transient strings can be logged.
   *
   * Logger ensures that allocated buffers are freed when log message is
   * processed.
   */
  static void log_strdup_showcase(void)
  {
  	char transient_str[] = "transient_string";
  
  	printk("String logging showcase.\n");
  
  	LOG_INF("Logging transient string:%s", log_strdup(transient_str));
  
  	/* Overwrite transient string to show that the logger has a copy. */
  	transient_str[0] = '\0';
  }
  
  static void wait_on_log_flushed(void)
  {
  	while (log_buffered_cnt()) {
  		k_sleep(K_MSEC(5));
  	}
  }
  
  /**
   * @brief Function demonstrates how fast data can be logged.
   *
   * Messages are logged and counted in a loop for 2 ticks (same clock source as
   * the one used for logging timestamp). Based on that and known clock frequency,
   * logging bandwidth is calculated.
   */
  static void performance_showcase(void)
  {
  /* Arbitrary limit when LOG_IMMEDIATE is enabled. */
  #define LOG_IMMEDIATE_TEST_MESSAGES_LIMIT 50
  
  	volatile uint32_t current_timestamp;
  	volatile uint32_t start_timestamp;
  	uint32_t limit = COND_CODE_1(CONFIG_LOG_IMMEDIATE,
  			     (LOG_IMMEDIATE_TEST_MESSAGES_LIMIT),
  			     (CONFIG_LOG_BUFFER_SIZE / sizeof(struct log_msg)));
  	uint32_t per_sec;
  	uint32_t cnt = 0U;
  	uint32_t window = 2U;
  
  	printk("Logging performance showcase.\n");
  	wait_on_log_flushed();
  
  	do {
  		cnt = 0;
  		start_timestamp = timestamp_get();
  
  		while (start_timestamp == timestamp_get()) {
  	#if (CONFIG_ARCH_POSIX)
  			k_busy_wait(100);
  	#endif
  		}
  
  		start_timestamp = timestamp_get();
  
  		do {
  			LOG_INF("performance test - log message %d", cnt);
  			cnt++;
  			current_timestamp = timestamp_get();
  	#if (CONFIG_ARCH_POSIX)
  			k_busy_wait(100);
  	#endif
  		} while (current_timestamp < (start_timestamp + window));
  
  		wait_on_log_flushed();
  
  		/* If limit exceeded then some messages might be dropped which
  		 * degraded performance. Decrease window size.
  		 * If less than half of limit is reached then it means that
  		 * window can be increased to improve precision.
  		 */
  		if (cnt >= limit) {
  			if (window >= 2) {
  				window /= 2;
  			} else {
  				break;
  			}
  		} else if (cnt < (limit / 2)) {
  			window *= 2;
  		} else {
  			break;
  		}
  	} while (1);
  
  	per_sec = (cnt * timestamp_freq()) / window;
  	printk("Estimated logging capabilities: %d messages/second\n", per_sec);
  }
  
  static void external_log_system_showcase(void)
  {
  	printk("Logs from external logging system showcase.\n");
  
  	ext_log_system_log_adapt();
  
  	ext_log_system_foo();
  }
  
  static void log_demo_thread(void *p1, void *p2, void *p3)
  {
  	bool usermode = k_is_user_context();
  
  	k_sleep(K_MSEC(100));
  
  	printk("\n\t---=< RUNNING LOGGER DEMO FROM %s THREAD >=---\n\n",
  		(usermode) ? "USER" : "KERNEL");
  
  	module_logging_showcase();
  
  	instance_logging_showcase();
  
  	/* Re-enabling filters before processing.
  	 * Note: Same filters are used to for gathering logs and processing.
  	 */
  	log_filter_set(NULL, CONFIG_LOG_DOMAIN_ID,
  		       log_source_id_get(sample_module_name_get()),
  		       CONFIG_LOG_DEFAULT_LEVEL);
  
  	log_filter_set(NULL, CONFIG_LOG_DOMAIN_ID,
  		       log_source_id_get(INST1_NAME),
  		       CONFIG_LOG_DEFAULT_LEVEL);
  
  	log_filter_set(NULL, CONFIG_LOG_DOMAIN_ID,
  		       log_source_id_get(INST2_NAME),
  		       CONFIG_LOG_DEFAULT_LEVEL);
  
  	wait_on_log_flushed();
  
  	log_strdup_showcase();
  
  	severity_levels_showcase();
  
  	wait_on_log_flushed();
  
  	if (!usermode) {
  		/*
  		 * Logger performance in user mode cannot be demonstrated
  		 * as precise timing API is accessible only from the kernel.
  		 */
  		performance_showcase();
  		wait_on_log_flushed();
  
  	}
  
  	external_log_system_showcase();
  	wait_on_log_flushed();
  }
  
  static void log_demo_supervisor(void *p1, void *p2, void *p3)
  {
  	/* Timestamp function could be set only from kernel thread. */
  	(void)log_set_timestamp_func(timestamp_get, timestamp_freq());
  
  	log_demo_thread(p1, p2, p3);
  
  #ifdef CONFIG_USERSPACE
  	k_mem_domain_init(&app_domain, ARRAY_SIZE(app_parts), app_parts);
  	k_mem_domain_add_thread(&app_domain, k_current_get());
  	k_thread_user_mode_enter(log_demo_thread, p1, p2, p3);
  #endif
  }
  
  K_THREAD_DEFINE(log_demo_thread_id, STACKSIZE, log_demo_supervisor,
  		NULL, NULL, NULL,
  		K_LOWEST_APPLICATION_THREAD_PRIO, 0, 1);
  