backtrace.c 2.1 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091
  1. /*
  2. * SH specific backtracing code for oprofile
  3. *
  4. * Copyright 2007 STMicroelectronics Ltd.
  5. *
  6. * Author: Dave Peverley <dpeverley@mpc-data.co.uk>
  7. *
  8. * Based on ARM oprofile backtrace code by Richard Purdie and in turn, i386
  9. * oprofile backtrace code by John Levon, David Smith
  10. *
  11. * This program is free software; you can redistribute it and/or modify
  12. * it under the terms of the GNU General Public License version 2 as
  13. * published by the Free Software Foundation.
  14. *
  15. */
  16. #include <linux/oprofile.h>
  17. #include <linux/sched.h>
  18. #include <linux/kallsyms.h>
  19. #include <linux/mm.h>
  20. #include <asm/unwinder.h>
  21. #include <asm/ptrace.h>
  22. #include <asm/uaccess.h>
  23. #include <asm/sections.h>
  24. #include <asm/stacktrace.h>
  25. static int backtrace_stack(void *data, char *name)
  26. {
  27. /* Yes, we want all stacks */
  28. return 0;
  29. }
  30. static void backtrace_address(void *data, unsigned long addr, int reliable)
  31. {
  32. unsigned int *depth = data;
  33. if ((*depth)--)
  34. oprofile_add_trace(addr);
  35. }
  36. static struct stacktrace_ops backtrace_ops = {
  37. .stack = backtrace_stack,
  38. .address = backtrace_address,
  39. };
  40. /* Limit to stop backtracing too far. */
  41. static int backtrace_limit = 20;
  42. static unsigned long *
  43. user_backtrace(unsigned long *stackaddr, struct pt_regs *regs)
  44. {
  45. unsigned long buf_stack;
  46. /* Also check accessibility of address */
  47. if (!access_ok(VERIFY_READ, stackaddr, sizeof(unsigned long)))
  48. return NULL;
  49. if (__copy_from_user_inatomic(&buf_stack, stackaddr, sizeof(unsigned long)))
  50. return NULL;
  51. /* Quick paranoia check */
  52. if (buf_stack & 3)
  53. return NULL;
  54. oprofile_add_trace(buf_stack);
  55. stackaddr++;
  56. return stackaddr;
  57. }
  58. void sh_backtrace(struct pt_regs * const regs, unsigned int depth)
  59. {
  60. unsigned long *stackaddr;
  61. /*
  62. * Paranoia - clip max depth as we could get lost in the weeds.
  63. */
  64. if (depth > backtrace_limit)
  65. depth = backtrace_limit;
  66. stackaddr = (unsigned long *)kernel_stack_pointer(regs);
  67. if (!user_mode(regs)) {
  68. if (depth)
  69. unwind_stack(NULL, regs, stackaddr,
  70. &backtrace_ops, &depth);
  71. return;
  72. }
  73. while (depth-- && (stackaddr != NULL))
  74. stackaddr = user_backtrace(stackaddr, regs);
  75. }