diff options
| author | Linus Torvalds <torvalds@linux-foundation.org> | 2012-01-06 11:02:58 -0500 |
|---|---|---|
| committer | Linus Torvalds <torvalds@linux-foundation.org> | 2012-01-06 11:02:58 -0500 |
| commit | 35b740e4662ef386f0c60e1b60aaf5b44db9914c (patch) | |
| tree | 502a8f9499bc1b4cb3300d666dab2d01a1921224 /kernel/trace | |
| parent | 423d091dfe58d3109d84c408810a7cfa82f6f184 (diff) | |
| parent | 9e183426bfb52bb44bf3c443d6587e4d02478603 (diff) | |
Merge branch 'perf-core-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip
* 'perf-core-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip: (106 commits)
perf kvm: Fix copy & paste error in description
perf script: Kill script_spec__delete
perf top: Fix a memory leak
perf stat: Introduce get_ratio_color() helper
perf session: Remove impossible condition check
perf tools: Fix feature-bits rework fallout, remove unused variable
perf script: Add generic perl handler to process events
perf tools: Use for_each_set_bit() to iterate over feature flags
perf tools: Unify handling of features when writing feature section
perf report: Accept fifos as input file
perf tools: Moving code in some files
perf tools: Fix out-of-bound access to struct perf_session
perf tools: Continue processing header on unknown features
perf tools: Improve macros for struct feature_ops
perf: builtin-record: Document and check that mmap_pages must be a power of two.
perf: builtin-record: Provide advice if mmap'ing fails with EPERM.
perf tools: Fix truncated annotation
perf script: look up thread using tid instead of pid
perf tools: Look up thread names for system wide profiling
perf tools: Fix comm for processes with named threads
...
Diffstat (limited to 'kernel/trace')
| -rw-r--r-- | kernel/trace/trace.c | 105 | ||||
| -rw-r--r-- | kernel/trace/trace.h | 2 | ||||
| -rw-r--r-- | kernel/trace/trace_events_filter.c | 26 | ||||
| -rw-r--r-- | kernel/trace/trace_irqsoff.c | 13 | ||||
| -rw-r--r-- | kernel/trace/trace_output.c | 16 | ||||
| -rw-r--r-- | kernel/trace/trace_sched_wakeup.c | 13 |
6 files changed, 138 insertions, 37 deletions
diff --git a/kernel/trace/trace.c b/kernel/trace/trace.c index a043d224adf6..91dc4bc8bf72 100644 --- a/kernel/trace/trace.c +++ b/kernel/trace/trace.c | |||
| @@ -338,7 +338,8 @@ static DECLARE_WAIT_QUEUE_HEAD(trace_wait); | |||
| 338 | /* trace_flags holds trace_options default values */ | 338 | /* trace_flags holds trace_options default values */ |
| 339 | unsigned long trace_flags = TRACE_ITER_PRINT_PARENT | TRACE_ITER_PRINTK | | 339 | unsigned long trace_flags = TRACE_ITER_PRINT_PARENT | TRACE_ITER_PRINTK | |
| 340 | TRACE_ITER_ANNOTATE | TRACE_ITER_CONTEXT_INFO | TRACE_ITER_SLEEP_TIME | | 340 | TRACE_ITER_ANNOTATE | TRACE_ITER_CONTEXT_INFO | TRACE_ITER_SLEEP_TIME | |
| 341 | TRACE_ITER_GRAPH_TIME | TRACE_ITER_RECORD_CMD | TRACE_ITER_OVERWRITE; | 341 | TRACE_ITER_GRAPH_TIME | TRACE_ITER_RECORD_CMD | TRACE_ITER_OVERWRITE | |
| 342 | TRACE_ITER_IRQ_INFO; | ||
| 342 | 343 | ||
| 343 | static int trace_stop_count; | 344 | static int trace_stop_count; |
| 344 | static DEFINE_RAW_SPINLOCK(tracing_start_lock); | 345 | static DEFINE_RAW_SPINLOCK(tracing_start_lock); |
| @@ -426,6 +427,7 @@ static const char *trace_options[] = { | |||
| 426 | "record-cmd", | 427 | "record-cmd", |
| 427 | "overwrite", | 428 | "overwrite", |
| 428 | "disable_on_free", | 429 | "disable_on_free", |
| 430 | "irq-info", | ||
| 429 | NULL | 431 | NULL |
| 430 | }; | 432 | }; |
| 431 | 433 | ||
| @@ -1843,6 +1845,33 @@ static void s_stop(struct seq_file *m, void *p) | |||
| 1843 | trace_event_read_unlock(); | 1845 | trace_event_read_unlock(); |
| 1844 | } | 1846 | } |
| 1845 | 1847 | ||
| 1848 | static void | ||
| 1849 | get_total_entries(struct trace_array *tr, unsigned long *total, unsigned long *entries) | ||
| 1850 | { | ||
| 1851 | unsigned long count; | ||
| 1852 | int cpu; | ||
| 1853 | |||
| 1854 | *total = 0; | ||
| 1855 | *entries = 0; | ||
| 1856 | |||
| 1857 | for_each_tracing_cpu(cpu) { | ||
| 1858 | count = ring_buffer_entries_cpu(tr->buffer, cpu); | ||
| 1859 | /* | ||
| 1860 | * If this buffer has skipped entries, then we hold all | ||
| 1861 | * entries for the trace and we need to ignore the | ||
| 1862 | * ones before the time stamp. | ||
| 1863 | */ | ||
| 1864 | if (tr->data[cpu]->skipped_entries) { | ||
| 1865 | count -= tr->data[cpu]->skipped_entries; | ||
| 1866 | /* total is the same as the entries */ | ||
| 1867 | *total += count; | ||
| 1868 | } else | ||
| 1869 | *total += count + | ||
| 1870 | ring_buffer_overrun_cpu(tr->buffer, cpu); | ||
| 1871 | *entries += count; | ||
| 1872 | } | ||
| 1873 | } | ||
| 1874 | |||
| 1846 | static void print_lat_help_header(struct seq_file *m) | 1875 | static void print_lat_help_header(struct seq_file *m) |
| 1847 | { | 1876 | { |
| 1848 | seq_puts(m, "# _------=> CPU# \n"); | 1877 | seq_puts(m, "# _------=> CPU# \n"); |
| @@ -1855,12 +1884,35 @@ static void print_lat_help_header(struct seq_file *m) | |||
| 1855 | seq_puts(m, "# \\ / ||||| \\ | / \n"); | 1884 | seq_puts(m, "# \\ / ||||| \\ | / \n"); |
| 1856 | } | 1885 | } |
| 1857 | 1886 | ||
| 1858 | static void print_func_help_header(struct seq_file *m) | 1887 | static void print_event_info(struct trace_array *tr, struct seq_file *m) |
| 1888 | { | ||
| 1889 | unsigned long total; | ||
| 1890 | unsigned long entries; | ||
| 1891 | |||
| 1892 | get_total_entries(tr, &total, &entries); | ||
| 1893 | seq_printf(m, "# entries-in-buffer/entries-written: %lu/%lu #P:%d\n", | ||
| 1894 | entries, total, num_online_cpus()); | ||
| 1895 | seq_puts(m, "#\n"); | ||
| 1896 | } | ||
| 1897 | |||
| 1898 | static void print_func_help_header(struct trace_array *tr, struct seq_file *m) | ||
| 1859 | { | 1899 | { |
| 1860 | seq_puts(m, "# TASK-PID CPU# TIMESTAMP FUNCTION\n"); | 1900 | print_event_info(tr, m); |
| 1901 | seq_puts(m, "# TASK-PID CPU# TIMESTAMP FUNCTION\n"); | ||
| 1861 | seq_puts(m, "# | | | | |\n"); | 1902 | seq_puts(m, "# | | | | |\n"); |
| 1862 | } | 1903 | } |
| 1863 | 1904 | ||
| 1905 | static void print_func_help_header_irq(struct trace_array *tr, struct seq_file *m) | ||
| 1906 | { | ||
| 1907 | print_event_info(tr, m); | ||
| 1908 | seq_puts(m, "# _-----=> irqs-off\n"); | ||
| 1909 | seq_puts(m, "# / _----=> need-resched\n"); | ||
| 1910 | seq_puts(m, "# | / _---=> hardirq/softirq\n"); | ||
| 1911 | seq_puts(m, "# || / _--=> preempt-depth\n"); | ||
| 1912 | seq_puts(m, "# ||| / delay\n"); | ||
| 1913 | seq_puts(m, "# TASK-PID CPU# |||| TIMESTAMP FUNCTION\n"); | ||
| 1914 | seq_puts(m, "# | | | |||| | |\n"); | ||
| 1915 | } | ||
| 1864 | 1916 | ||
| 1865 | void | 1917 | void |
| 1866 | print_trace_header(struct seq_file *m, struct trace_iterator *iter) | 1918 | print_trace_header(struct seq_file *m, struct trace_iterator *iter) |
| @@ -1869,32 +1921,14 @@ print_trace_header(struct seq_file *m, struct trace_iterator *iter) | |||
| 1869 | struct trace_array *tr = iter->tr; | 1921 | struct trace_array *tr = iter->tr; |
| 1870 | struct trace_array_cpu *data = tr->data[tr->cpu]; | 1922 | struct trace_array_cpu *data = tr->data[tr->cpu]; |
| 1871 | struct tracer *type = current_trace; | 1923 | struct tracer *type = current_trace; |
| 1872 | unsigned long entries = 0; | 1924 | unsigned long entries; |
| 1873 | unsigned long total = 0; | 1925 | unsigned long total; |
| 1874 | unsigned long count; | ||
| 1875 | const char *name = "preemption"; | 1926 | const char *name = "preemption"; |
| 1876 | int cpu; | ||
| 1877 | 1927 | ||
| 1878 | if (type) | 1928 | if (type) |
| 1879 | name = type->name; | 1929 | name = type->name; |
| 1880 | 1930 | ||
| 1881 | 1931 | get_total_entries(tr, &total, &entries); | |
| 1882 | for_each_tracing_cpu(cpu) { | ||
| 1883 | count = ring_buffer_entries_cpu(tr->buffer, cpu); | ||
| 1884 | /* | ||
| 1885 | * If this buffer has skipped entries, then we hold all | ||
| 1886 | * entries for the trace and we need to ignore the | ||
| 1887 | * ones before the time stamp. | ||
| 1888 | */ | ||
| 1889 | if (tr->data[cpu]->skipped_entries) { | ||
| 1890 | count -= tr->data[cpu]->skipped_entries; | ||
| 1891 | /* total is the same as the entries */ | ||
| 1892 | total += count; | ||
| 1893 | } else | ||
| 1894 | total += count + | ||
| 1895 | ring_buffer_overrun_cpu(tr->buffer, cpu); | ||
| 1896 | entries += count; | ||
| 1897 | } | ||
| 1898 | 1932 | ||
| 1899 | seq_printf(m, "# %s latency trace v1.1.5 on %s\n", | 1933 | seq_printf(m, "# %s latency trace v1.1.5 on %s\n", |
| 1900 | name, UTS_RELEASE); | 1934 | name, UTS_RELEASE); |
| @@ -2140,6 +2174,21 @@ enum print_line_t print_trace_line(struct trace_iterator *iter) | |||
| 2140 | return print_trace_fmt(iter); | 2174 | return print_trace_fmt(iter); |
| 2141 | } | 2175 | } |
| 2142 | 2176 | ||
| 2177 | void trace_latency_header(struct seq_file *m) | ||
| 2178 | { | ||
| 2179 | struct trace_iterator *iter = m->private; | ||
| 2180 | |||
| 2181 | /* print nothing if the buffers are empty */ | ||
| 2182 | if (trace_empty(iter)) | ||
| 2183 | return; | ||
| 2184 | |||
| 2185 | if (iter->iter_flags & TRACE_FILE_LAT_FMT) | ||
| 2186 | print_trace_header(m, iter); | ||
| 2187 | |||
| 2188 | if (!(trace_flags & TRACE_ITER_VERBOSE)) | ||
| 2189 | print_lat_help_header(m); | ||
| 2190 | } | ||
| 2191 | |||
| 2143 | void trace_default_header(struct seq_file *m) | 2192 | void trace_default_header(struct seq_file *m) |
| 2144 | { | 2193 | { |
| 2145 | struct trace_iterator *iter = m->private; | 2194 | struct trace_iterator *iter = m->private; |
| @@ -2155,8 +2204,12 @@ void trace_default_header(struct seq_file *m) | |||
| 2155 | if (!(trace_flags & TRACE_ITER_VERBOSE)) | 2204 | if (!(trace_flags & TRACE_ITER_VERBOSE)) |
| 2156 | print_lat_help_header(m); | 2205 | print_lat_help_header(m); |
| 2157 | } else { | 2206 | } else { |
| 2158 | if (!(trace_flags & TRACE_ITER_VERBOSE)) | 2207 | if (!(trace_flags & TRACE_ITER_VERBOSE)) { |
| 2159 | print_func_help_header(m); | 2208 | if (trace_flags & TRACE_ITER_IRQ_INFO) |
| 2209 | print_func_help_header_irq(iter->tr, m); | ||
| 2210 | else | ||
| 2211 | print_func_help_header(iter->tr, m); | ||
| 2212 | } | ||
| 2160 | } | 2213 | } |
| 2161 | } | 2214 | } |
| 2162 | 2215 | ||
diff --git a/kernel/trace/trace.h b/kernel/trace/trace.h index 092e1f8d18dc..2c2657462ac3 100644 --- a/kernel/trace/trace.h +++ b/kernel/trace/trace.h | |||
| @@ -370,6 +370,7 @@ void trace_graph_function(struct trace_array *tr, | |||
| 370 | unsigned long ip, | 370 | unsigned long ip, |
| 371 | unsigned long parent_ip, | 371 | unsigned long parent_ip, |
| 372 | unsigned long flags, int pc); | 372 | unsigned long flags, int pc); |
| 373 | void trace_latency_header(struct seq_file *m); | ||
| 373 | void trace_default_header(struct seq_file *m); | 374 | void trace_default_header(struct seq_file *m); |
| 374 | void print_trace_header(struct seq_file *m, struct trace_iterator *iter); | 375 | void print_trace_header(struct seq_file *m, struct trace_iterator *iter); |
| 375 | int trace_empty(struct trace_iterator *iter); | 376 | int trace_empty(struct trace_iterator *iter); |
| @@ -654,6 +655,7 @@ enum trace_iterator_flags { | |||
| 654 | TRACE_ITER_RECORD_CMD = 0x100000, | 655 | TRACE_ITER_RECORD_CMD = 0x100000, |
| 655 | TRACE_ITER_OVERWRITE = 0x200000, | 656 | TRACE_ITER_OVERWRITE = 0x200000, |
| 656 | TRACE_ITER_STOP_ON_FREE = 0x400000, | 657 | TRACE_ITER_STOP_ON_FREE = 0x400000, |
| 658 | TRACE_ITER_IRQ_INFO = 0x800000, | ||
| 657 | }; | 659 | }; |
| 658 | 660 | ||
| 659 | /* | 661 | /* |
diff --git a/kernel/trace/trace_events_filter.c b/kernel/trace/trace_events_filter.c index 95dc31efd6dd..f04cc3136bd3 100644 --- a/kernel/trace/trace_events_filter.c +++ b/kernel/trace/trace_events_filter.c | |||
| @@ -27,6 +27,12 @@ | |||
| 27 | #include "trace.h" | 27 | #include "trace.h" |
| 28 | #include "trace_output.h" | 28 | #include "trace_output.h" |
| 29 | 29 | ||
| 30 | #define DEFAULT_SYS_FILTER_MESSAGE \ | ||
| 31 | "### global filter ###\n" \ | ||
| 32 | "# Use this to set filters for multiple events.\n" \ | ||
| 33 | "# Only events with the given fields will be affected.\n" \ | ||
| 34 | "# If no events are modified, an error message will be displayed here" | ||
| 35 | |||
| 30 | enum filter_op_ids | 36 | enum filter_op_ids |
| 31 | { | 37 | { |
| 32 | OP_OR, | 38 | OP_OR, |
| @@ -646,7 +652,7 @@ void print_subsystem_event_filter(struct event_subsystem *system, | |||
| 646 | if (filter && filter->filter_string) | 652 | if (filter && filter->filter_string) |
| 647 | trace_seq_printf(s, "%s\n", filter->filter_string); | 653 | trace_seq_printf(s, "%s\n", filter->filter_string); |
| 648 | else | 654 | else |
| 649 | trace_seq_printf(s, "none\n"); | 655 | trace_seq_printf(s, DEFAULT_SYS_FILTER_MESSAGE "\n"); |
| 650 | mutex_unlock(&event_mutex); | 656 | mutex_unlock(&event_mutex); |
| 651 | } | 657 | } |
| 652 | 658 | ||
| @@ -1838,7 +1844,10 @@ int apply_subsystem_event_filter(struct event_subsystem *system, | |||
| 1838 | if (!filter) | 1844 | if (!filter) |
| 1839 | goto out; | 1845 | goto out; |
| 1840 | 1846 | ||
| 1841 | replace_filter_string(filter, filter_string); | 1847 | /* System filters just show a default message */ |
| 1848 | kfree(filter->filter_string); | ||
| 1849 | filter->filter_string = NULL; | ||
| 1850 | |||
| 1842 | /* | 1851 | /* |
| 1843 | * No event actually uses the system filter | 1852 | * No event actually uses the system filter |
| 1844 | * we can free it without synchronize_sched(). | 1853 | * we can free it without synchronize_sched(). |
| @@ -1848,14 +1857,12 @@ int apply_subsystem_event_filter(struct event_subsystem *system, | |||
| 1848 | 1857 | ||
| 1849 | parse_init(ps, filter_ops, filter_string); | 1858 | parse_init(ps, filter_ops, filter_string); |
| 1850 | err = filter_parse(ps); | 1859 | err = filter_parse(ps); |
| 1851 | if (err) { | 1860 | if (err) |
| 1852 | append_filter_err(ps, system->filter); | 1861 | goto err_filter; |
| 1853 | goto out; | ||
| 1854 | } | ||
| 1855 | 1862 | ||
| 1856 | err = replace_system_preds(system, ps, filter_string); | 1863 | err = replace_system_preds(system, ps, filter_string); |
| 1857 | if (err) | 1864 | if (err) |
| 1858 | append_filter_err(ps, system->filter); | 1865 | goto err_filter; |
| 1859 | 1866 | ||
| 1860 | out: | 1867 | out: |
| 1861 | filter_opstack_clear(ps); | 1868 | filter_opstack_clear(ps); |
| @@ -1865,6 +1872,11 @@ out_unlock: | |||
| 1865 | mutex_unlock(&event_mutex); | 1872 | mutex_unlock(&event_mutex); |
| 1866 | 1873 | ||
| 1867 | return err; | 1874 | return err; |
| 1875 | |||
| 1876 | err_filter: | ||
| 1877 | replace_filter_string(filter, filter_string); | ||
| 1878 | append_filter_err(ps, system->filter); | ||
| 1879 | goto out; | ||
| 1868 | } | 1880 | } |
| 1869 | 1881 | ||
| 1870 | #ifdef CONFIG_PERF_EVENTS | 1882 | #ifdef CONFIG_PERF_EVENTS |
diff --git a/kernel/trace/trace_irqsoff.c b/kernel/trace/trace_irqsoff.c index 20dad0d7a163..99d20e920368 100644 --- a/kernel/trace/trace_irqsoff.c +++ b/kernel/trace/trace_irqsoff.c | |||
| @@ -280,9 +280,20 @@ static enum print_line_t irqsoff_print_line(struct trace_iterator *iter) | |||
| 280 | } | 280 | } |
| 281 | 281 | ||
| 282 | static void irqsoff_graph_return(struct ftrace_graph_ret *trace) { } | 282 | static void irqsoff_graph_return(struct ftrace_graph_ret *trace) { } |
| 283 | static void irqsoff_print_header(struct seq_file *s) { } | ||
| 284 | static void irqsoff_trace_open(struct trace_iterator *iter) { } | 283 | static void irqsoff_trace_open(struct trace_iterator *iter) { } |
| 285 | static void irqsoff_trace_close(struct trace_iterator *iter) { } | 284 | static void irqsoff_trace_close(struct trace_iterator *iter) { } |
| 285 | |||
| 286 | #ifdef CONFIG_FUNCTION_TRACER | ||
| 287 | static void irqsoff_print_header(struct seq_file *s) | ||
| 288 | { | ||
| 289 | trace_default_header(s); | ||
| 290 | } | ||
| 291 | #else | ||
| 292 | static void irqsoff_print_header(struct seq_file *s) | ||
| 293 | { | ||
| 294 | trace_latency_header(s); | ||
| 295 | } | ||
| 296 | #endif /* CONFIG_FUNCTION_TRACER */ | ||
| 286 | #endif /* CONFIG_FUNCTION_GRAPH_TRACER */ | 297 | #endif /* CONFIG_FUNCTION_GRAPH_TRACER */ |
| 287 | 298 | ||
| 288 | /* | 299 | /* |
diff --git a/kernel/trace/trace_output.c b/kernel/trace/trace_output.c index 51999309a6cf..0d6ff3555942 100644 --- a/kernel/trace/trace_output.c +++ b/kernel/trace/trace_output.c | |||
| @@ -627,11 +627,23 @@ int trace_print_context(struct trace_iterator *iter) | |||
| 627 | unsigned long usec_rem = do_div(t, USEC_PER_SEC); | 627 | unsigned long usec_rem = do_div(t, USEC_PER_SEC); |
| 628 | unsigned long secs = (unsigned long)t; | 628 | unsigned long secs = (unsigned long)t; |
| 629 | char comm[TASK_COMM_LEN]; | 629 | char comm[TASK_COMM_LEN]; |
| 630 | int ret; | ||
| 630 | 631 | ||
| 631 | trace_find_cmdline(entry->pid, comm); | 632 | trace_find_cmdline(entry->pid, comm); |
| 632 | 633 | ||
| 633 | return trace_seq_printf(s, "%16s-%-5d [%03d] %5lu.%06lu: ", | 634 | ret = trace_seq_printf(s, "%16s-%-5d [%03d] ", |
| 634 | comm, entry->pid, iter->cpu, secs, usec_rem); | 635 | comm, entry->pid, iter->cpu); |
| 636 | if (!ret) | ||
| 637 | return 0; | ||
| 638 | |||
| 639 | if (trace_flags & TRACE_ITER_IRQ_INFO) { | ||
| 640 | ret = trace_print_lat_fmt(s, entry); | ||
| 641 | if (!ret) | ||
| 642 | return 0; | ||
| 643 | } | ||
| 644 | |||
| 645 | return trace_seq_printf(s, " %5lu.%06lu: ", | ||
| 646 | secs, usec_rem); | ||
| 635 | } | 647 | } |
| 636 | 648 | ||
| 637 | int trace_print_lat_context(struct trace_iterator *iter) | 649 | int trace_print_lat_context(struct trace_iterator *iter) |
diff --git a/kernel/trace/trace_sched_wakeup.c b/kernel/trace/trace_sched_wakeup.c index e4a70c0c71b6..ff791ea48b57 100644 --- a/kernel/trace/trace_sched_wakeup.c +++ b/kernel/trace/trace_sched_wakeup.c | |||
| @@ -280,9 +280,20 @@ static enum print_line_t wakeup_print_line(struct trace_iterator *iter) | |||
| 280 | } | 280 | } |
| 281 | 281 | ||
| 282 | static void wakeup_graph_return(struct ftrace_graph_ret *trace) { } | 282 | static void wakeup_graph_return(struct ftrace_graph_ret *trace) { } |
| 283 | static void wakeup_print_header(struct seq_file *s) { } | ||
| 284 | static void wakeup_trace_open(struct trace_iterator *iter) { } | 283 | static void wakeup_trace_open(struct trace_iterator *iter) { } |
| 285 | static void wakeup_trace_close(struct trace_iterator *iter) { } | 284 | static void wakeup_trace_close(struct trace_iterator *iter) { } |
| 285 | |||
| 286 | #ifdef CONFIG_FUNCTION_TRACER | ||
| 287 | static void wakeup_print_header(struct seq_file *s) | ||
| 288 | { | ||
| 289 | trace_default_header(s); | ||
| 290 | } | ||
| 291 | #else | ||
| 292 | static void wakeup_print_header(struct seq_file *s) | ||
| 293 | { | ||
| 294 | trace_latency_header(s); | ||
| 295 | } | ||
| 296 | #endif /* CONFIG_FUNCTION_TRACER */ | ||
| 286 | #endif /* CONFIG_FUNCTION_GRAPH_TRACER */ | 297 | #endif /* CONFIG_FUNCTION_GRAPH_TRACER */ |
| 287 | 298 | ||
| 288 | /* | 299 | /* |
