summaryrefslogtreecommitdiff
path: root/release/src/linux/linux/include/asm-mips/kernprof.h
blob: dbd7a156e3d974029513d60be1e353cebb917b74 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
/*
 * Copyright (C) 2000 Silicon Graphics, Inc.
 *
 * Written by Ulf Carlsson (ulfc@engr.sgi.com)
 */

#ifndef _ASM_KERNPROF_H
#define _ASM_KERNPROF_H

#ifdef __KERNEL__

#include <asm/system.h>
#include <asm/ptrace.h>
#include <asm/processor.h>

#define DFL_PC_RES 4		/* default PC resolution for this platform */

#define in_firmware(regs) 0	/* never in the PROM during normal execution */

extern char stext;
extern char _etext;
extern int prof_freq[];

extern int setup_profiling_timer(unsigned int);

typedef struct frame_info frame_info_t;

struct frame_info {
	unsigned long ra;
	unsigned long pc;
	unsigned long sp;
	unsigned long top;
};

#define frame_get_pc(p)		((p)->pc)

/*
 * A function tests up its stack frame with the instructions
 *
 *	addiu	$sp,$sp,-stacksize
 *	sw	$ra,stacksize-8($sp)
 *
 * The timer interrupt may arrive at any time including right at the moment
 * that the new frame is being set up, so we need to distinguish a few cases.
 */
static __inline__ void get_top_frame(struct pt_regs *regs, frame_info_t *p)
{
	unsigned long pc = regs->cp0_epc;

	pc = regs->cp0_epc;

#ifndef CONFIG_SMP
	{
		extern unsigned long kernelsp;
		p->top = kernelsp;
	}
#else
	{
		unsigned int lo, hi;
		lo = read_32bit_cp0_register(CP0_WATCHLO);
		hi = read_32bit_cp0_register(CP0_WATCHHI);
		p->top = ((unsigned long) hi << 32) | lo;
	}
#endif

	do {
		unsigned int inst = *(unsigned int *)pc;
		/* First we look for a ``addiu $sp,$sp,...'' and then we look
		   for a ``jr $ra'' in case this is a leaf function without
		   stack frame.  */
		if ((inst & 0xffff0000) == 0x27bd0000) {
			p->sp = regs->regs[29] - (short) (inst & 0xffff);
			p->ra = *((unsigned long *)p->sp - 1);
			p->pc = regs->cp0_epc;
			return;
		} else if (inst == 0x03e00008) { 
			/* N32 says that routines aren't restricted to a single
			   exit block.  In that case we lose.  The thing is
			   that the .mdebug format doesn't handle that either
			   so we should be pretty safe.  */
			p->sp = regs->regs[29];
			p->ra = regs->regs[31];
			p->pc = regs->cp0_epc;
		}
	} while (--pc > (unsigned long) &stext);

	BUG();
}

static unsigned long this_pc(void)
{
	return (unsigned long)return_address();
}

/* Fabricate a stack frame that is sufficient to begin walking up the stack */
static __inline__ int build_fake_frame(frame_info_t *p)
{
#ifndef CONFIG_SMP
	{
		extern unsigned long kernelsp;
		p->top = kernelsp;
	}
#else
	{
		unsigned int lo, hi;
		lo = read_32bit_cp0_register(CP0_WATCHLO);
		hi = read_32bit_cp0_register(CP0_WATCHHI);
		p->top = ((unsigned long) hi << 32) | lo;
	}
#endif
	__asm__ __volatile__("sw\t$29,%0\t\n" : "=m" (p->sp));
	p->pc = this_pc();
	return 1;
}

static __inline__ int last_frame(frame_info_t *p)
{
	if (p->sp < (unsigned long) current + sizeof(*current))
		BUG();
	
	return (p->sp < p->top);
}

static __inline__ int get_next_frame(frame_info_t *p)
{
	unsigned int *sp = (unsigned int *)p->sp;
	unsigned int *pc = (unsigned int *)p->pc;

	if (last_frame(p))
		return 0;

	/*
	 * First, scan backwards to find the stack-decrement that signals the
	 * beginning of this routine in which we're inlined.  That tells us
	 * how to roll back the stack.
	 */
	do {
		unsigned int inst = *pc;
		/* Look for a ``addiu $sp,$sp,...'' */
		if ((inst & 0xffff0000) == 0x27bd0000) {
			p->sp = (unsigned long)sp - (short) (inst & 0xffff);
			break;
		}
	} while (--pc > (unsigned int *)&stext);

	if (pc == (unsigned int *)&stext)
		return 0;

	/*
	 * Now scan forwards to find the $ra-save, so we can decode the
	 * instruction and retrieve the return address from the stack.
	 */
	pc++;
	do {
		unsigned int inst = *pc;
		/* Look for a ``sw $ra,NN($sp)'' */
		if ((inst & 0xffff0000) == 0xafbf0000) {
			p->pc = *(unsigned long *)((unsigned long)sp + (short)(inst & 0xffff));
			return 1;
		}
	} while (++pc <= (unsigned int *)&_etext);

	return 0;
}

#define supports_call_graph prof_have_mcount

#define get_prof_freq() prof_freq[0]

/* No performance counters for the time being */

#define have_perfctr() 0
#define valid_perfctr_event(e) 0
#define valid_perfctr_freq(n) 0
#define perfctr_reload(x)
#define __perfctr_stop()
#define __perfctr_commence(x,y)

#endif /* __KERNEL__ */

#endif /* !_ASM_KERNPROF_H */