profiling in user mode.
[akaros.git] / kern / include / ros / procinfo.h
1 /* See COPYRIGHT for copyright information. */
2
3 #ifndef ROS_PROCINFO_H
4 #define ROS_PROCINFO_H
5
6 #include <ros/memlayout.h>
7 #include <ros/common.h>
8 #include <ros/resource.h>
9 #include <ros/atomic.h>
10 #include <ros/arch/arch.h>
11 #include <string.h>
12
13 #define PROCINFO_MAX_ARGP 32
14 #define PROCINFO_ARGBUF_SIZE 3072
15
16 #ifdef ROS_KERNEL
17 #include <sys/queue.h>
18 #endif /* ROS_KERNEL */
19
20 /* Not necessary to expose all of this, but it doesn't hurt, and is convenient
21  * for the kernel.  Need to do some acrobatics for the TAILQ_ENTRY. */
22 struct vcore;
23 struct vcore {
24 #ifdef ROS_KERNEL
25         TAILQ_ENTRY(vcore)      list;
26 #else /* userspace */
27         void                            *dummy_ptr1;
28         void                            *dummy_ptr2;
29 #endif /* ROS_KERNEL */
30         uint32_t                        pcoreid;
31         bool                            valid;
32         uint32_t                        nr_preempts_sent;       /* these two differ when a preempt*/
33         uint32_t                        nr_preempts_done;       /* is in flight. */
34         uint64_t                        preempt_pending;
35         /* for profiling. Store nsecs into 'resume' each time we start.
36          * Each time we stop, subtract resume from current nsecs and add it to total.
37          * This way, a process can see cumulative runtime as of the last
38          * resume, and can also calculate runtime in this interval, by adding
39          * (ns - resume) + total. This assumes we have an easy way to get
40          * ns at the application level.
41          */
42         uint64_t                        resume; 
43         uint64_t                        total;
44 };
45
46 struct pcore {
47         uint32_t                        vcoreid;
48         bool                            valid;
49 };
50
51 typedef struct procinfo {
52         pid_t pid;
53         pid_t ppid;
54         size_t max_vcores;      /* TODO: change to a uint32_t */
55         uint64_t tsc_freq;
56         uint64_t timing_overhead;
57         void *heap_bottom;
58         /* for traditional forks, these two need to be memcpy'd over: */
59         char *argp[PROCINFO_MAX_ARGP];
60         char argbuf[PROCINFO_ARGBUF_SIZE];
61         /* glibc relies on stuff above this point.  if you change it, you need to
62          * rebuild glibc. */
63         bool is_mcp;                    /* is in multi mode */
64         unsigned long           res_grant[MAX_NUM_RESOURCES];
65         struct vcore            vcoremap[MAX_NUM_CPUS];
66         uint32_t                        num_vcores;
67         struct pcore            pcoremap[MAX_NUM_CPUS];
68         seq_ctr_t                       coremap_seqctr;
69 } procinfo_t;
70 #define PROCINFO_NUM_PAGES  ((sizeof(procinfo_t)-1)/PGSIZE + 1) 
71
72 static int
73 procinfo_pack_args(procinfo_t* p, char* const* argv, char* const* envp)
74 {
75         int nargv = 0, nenvp = 0;
76         if(argv) while(argv[nargv]) nargv++;
77         if(envp) while(envp[nenvp]) nenvp++;
78
79         if(nargv+nenvp+2 > PROCINFO_MAX_ARGP)
80                 return -1;
81
82         int pos = 0;
83         int i;
84         for(i = 0; i < nargv; i++)
85         {
86                 int len = strlen(argv[i])+1;
87                 if(pos+len > PROCINFO_ARGBUF_SIZE)
88                         return -1;
89                 p->argp[i] = ((procinfo_t*)UINFO)->argbuf+pos;
90                 memcpy(p->argbuf+pos,argv[i],len);
91                 pos += len;
92         }
93         p->argp[nargv] = 0;
94
95         for(i = 0; i < nenvp; i++)
96         {
97                 int len = strlen(envp[i])+1;
98                 if(pos+len > PROCINFO_ARGBUF_SIZE)
99                         return -1;
100                 p->argp[nargv+1+i] = ((procinfo_t*)UINFO)->argbuf+pos;
101                 memcpy(p->argbuf+pos,envp[i],len);
102                 pos += len;
103         }
104         p->argp[nargv+nenvp+1] = 0;
105         
106         return 0;
107 }
108
109 // this is how user programs access the procinfo page
110 #ifndef ROS_KERNEL
111 # define __procinfo (*(procinfo_t*)UINFO)
112
113 #include <ros/common.h>
114 #include <ros/atomic.h>
115 #include <ros/syscall.h>
116
117 /* Figure out what your vcoreid is from your pcoreid and procinfo.  Only low
118  * level or debugging code should call this. */
119 static inline uint32_t __get_vcoreid_from_procinfo(void)
120 {
121         /* The assumption is that any IPIs/KMSGs would knock userspace into the
122          * kernel before it could read the closing of the seqctr.  Put another way,
123          * there is a 'memory barrier' between the IPI write and the seqctr write.
124          * I think this is true. */
125         uint32_t kpcoreid, kvcoreid;
126         seq_ctr_t old_seq;
127         do {
128                 cmb();
129                 old_seq = __procinfo.coremap_seqctr;
130                 kpcoreid = __ros_syscall_noerrno(SYS_getpcoreid, 0, 0, 0, 0, 0, 0);
131                 if (!__procinfo.pcoremap[kpcoreid].valid)
132                         continue;
133                 kvcoreid = __procinfo.pcoremap[kpcoreid].vcoreid;
134         } while (seqctr_retry(old_seq, __procinfo.coremap_seqctr));
135         return kvcoreid;
136 }
137
138 static inline uint32_t __get_vcoreid(void)
139 {
140         /* since sys_getvcoreid could lie (and might never change) */
141         return __get_vcoreid_from_procinfo();
142 }
143
144 #endif /* ifndef ROS_KERNEL */
145
146 #endif // !ROS_PROCDATA_H