cregit-Linux how code gets into the kernel

Release 4.14 arch/powerpc/kernel/ptrace.c

/*
 *  PowerPC version
 *    Copyright (C) 1995-1996 Gary Thomas (gdt@linuxppc.org)
 *
 *  Derived from "arch/m68k/kernel/ptrace.c"
 *  Copyright (C) 1994 by Hamish Macdonald
 *  Taken from linux/kernel/ptrace.c and modified for M680x0.
 *  linux/kernel/ptrace.c is by Ross Biro 1/23/92, edited by Linus Torvalds
 *
 * Modified by Cort Dougan (cort@hq.fsmlabs.com)
 * and Paul Mackerras (paulus@samba.org).
 *
 * This file is subject to the terms and conditions of the GNU General
 * Public License.  See the file README.legal in the main directory of
 * this archive for more details.
 */

#include <linux/kernel.h>
#include <linux/sched.h>
#include <linux/mm.h>
#include <linux/smp.h>
#include <linux/errno.h>
#include <linux/ptrace.h>
#include <linux/regset.h>
#include <linux/tracehook.h>
#include <linux/elf.h>
#include <linux/user.h>
#include <linux/security.h>
#include <linux/signal.h>
#include <linux/seccomp.h>
#include <linux/audit.h>
#include <trace/syscall.h>
#include <linux/hw_breakpoint.h>
#include <linux/perf_event.h>
#include <linux/context_tracking.h>

#include <linux/uaccess.h>
#include <asm/page.h>
#include <asm/pgtable.h>
#include <asm/switch_to.h>
#include <asm/tm.h>
#include <asm/asm-prototypes.h>


#define CREATE_TRACE_POINTS
#include <trace/events/syscalls.h>

/*
 * The parameter save area on the stack is used to store arguments being passed
 * to callee function and is located at fixed offset from stack pointer.
 */
#ifdef CONFIG_PPC32

#define PARAMETER_SAVE_AREA_OFFSET	24  
/* bytes */
#else /* CONFIG_PPC32 */

#define PARAMETER_SAVE_AREA_OFFSET	48  
/* bytes */
#endif


struct pt_regs_offset {
	
const char *name;
	
int offset;
};


#define STR(s)	#s			
/* convert to string */

#define REG_OFFSET_NAME(r) {.name = #r, .offset = offsetof(struct pt_regs, r)}

#define GPR_OFFSET_NAME(num)	\
	{.name = STR(r##num), .offset = offsetof(struct pt_regs, gpr[num])}, \
        {.name = STR(gpr##num), .offset = offsetof(struct pt_regs, gpr[num])}

#define REG_OFFSET_END {.name = NULL, .offset = 0}


#define TVSO(f)	(offsetof(struct thread_vr_state, f))

#define TFSO(f)	(offsetof(struct thread_fp_state, f))

#define TSO(f)	(offsetof(struct thread_struct, f))


static const struct pt_regs_offset regoffset_table[] = {
	GPR_OFFSET_NAME(0),
	GPR_OFFSET_NAME(1),
	GPR_OFFSET_NAME(2),
	GPR_OFFSET_NAME(3),
	GPR_OFFSET_NAME(4),
	GPR_OFFSET_NAME(5),
	GPR_OFFSET_NAME(6),
	GPR_OFFSET_NAME(7),
	GPR_OFFSET_NAME(8),
	GPR_OFFSET_NAME(9),
	GPR_OFFSET_NAME(10),
	GPR_OFFSET_NAME(11),
	GPR_OFFSET_NAME(12),
	GPR_OFFSET_NAME(13),
	GPR_OFFSET_NAME(14),
	GPR_OFFSET_NAME(15),
	GPR_OFFSET_NAME(16),
	GPR_OFFSET_NAME(17),
	GPR_OFFSET_NAME(18),
	GPR_OFFSET_NAME(19),
	GPR_OFFSET_NAME(20),
	GPR_OFFSET_NAME(21),
	GPR_OFFSET_NAME(22),
	GPR_OFFSET_NAME(23),
	GPR_OFFSET_NAME(24),
	GPR_OFFSET_NAME(25),
	GPR_OFFSET_NAME(26),
	GPR_OFFSET_NAME(27),
	GPR_OFFSET_NAME(28),
	GPR_OFFSET_NAME(29),
	GPR_OFFSET_NAME(30),
	GPR_OFFSET_NAME(31),
	REG_OFFSET_NAME(nip),
	REG_OFFSET_NAME(msr),
	REG_OFFSET_NAME(ctr),
	REG_OFFSET_NAME(link),
	REG_OFFSET_NAME(xer),
	REG_OFFSET_NAME(ccr),
#ifdef CONFIG_PPC64
	REG_OFFSET_NAME(softe),
#else
	REG_OFFSET_NAME(mq),
#endif
	REG_OFFSET_NAME(trap),
	REG_OFFSET_NAME(dar),
	REG_OFFSET_NAME(dsisr),
	REG_OFFSET_END,
};

#ifdef CONFIG_PPC_TRANSACTIONAL_MEM

static void flush_tmregs_to_thread(struct task_struct *tsk) { /* * If task is not current, it will have been flushed already to * it's thread_struct during __switch_to(). * * A reclaim flushes ALL the state or if not in TM save TM SPRs * in the appropriate thread structures from live. */ if ((!cpu_has_feature(CPU_FTR_TM)) || (tsk != current)) return; if (MSR_TM_SUSPENDED(mfmsr())) { tm_reclaim_current(TM_CAUSE_SIGNAL); } else { tm_enable(); tm_save_sprs(&(tsk->thread)); } }

Contributors

PersonTokensPropCommitsCommitProp
Gustavo Romero3456.67%266.67%
Cyril Bur2643.33%133.33%
Total60100.00%3100.00%

#else
static inline void flush_tmregs_to_thread(struct task_struct *tsk) { }

Contributors

PersonTokensPropCommitsCommitProp
Cyril Bur11100.00%1100.00%
Total11100.00%1100.00%

#endif /** * regs_query_register_offset() - query register offset from its name * @name: the name of a register * * regs_query_register_offset() returns the offset of a register in struct * pt_regs from its name. If the name is invalid, this returns -EINVAL; */
int regs_query_register_offset(const char *name) { const struct pt_regs_offset *roff; for (roff = regoffset_table; roff->name != NULL; roff++) if (!strcmp(roff->name, name)) return roff->offset; return -EINVAL; }

Contributors

PersonTokensPropCommitsCommitProp
Mahesh Salgaonkar52100.00%1100.00%
Total52100.00%1100.00%

/** * regs_query_register_name() - query register name from its offset * @offset: the offset of a register in struct pt_regs. * * regs_query_register_name() returns the name of a register from its * offset in struct pt_regs. If the @offset is invalid, this returns NULL; */
const char *regs_query_register_name(unsigned int offset) { const struct pt_regs_offset *roff; for (roff = regoffset_table; roff->name != NULL; roff++) if (roff->offset == offset) return roff->name; return NULL; }

Contributors

PersonTokensPropCommitsCommitProp
Mahesh Salgaonkar48100.00%1100.00%
Total48100.00%1100.00%

/* * does not yet catch signals sent when the child dies. * in exit.c or in signal.c. */ /* * Set of msr bits that gdb can change on behalf of a process. */ #ifdef CONFIG_PPC_ADV_DEBUG_REGS #define MSR_DEBUGCHANGE 0 #else #define MSR_DEBUGCHANGE (MSR_SE | MSR_BE) #endif /* * Max register writeable via put_reg */ #ifdef CONFIG_PPC32 #define PT_MAX_PUT_REG PT_MQ #else #define PT_MAX_PUT_REG PT_CCR #endif
static unsigned long get_user_msr(struct task_struct *task) { return task->thread.regs->msr | task->thread.fpexc_mode; }

Contributors

PersonTokensPropCommitsCommitProp
Roland McGrath27100.00%1100.00%
Total27100.00%1100.00%


static int set_user_msr(struct task_struct *task, unsigned long msr) { task->thread.regs->msr &= ~MSR_DEBUGCHANGE; task->thread.regs->msr |= msr & MSR_DEBUGCHANGE; return 0; }

Contributors

PersonTokensPropCommitsCommitProp
Roland McGrath41100.00%1100.00%
Total41100.00%1100.00%

#ifdef CONFIG_PPC_TRANSACTIONAL_MEM
static unsigned long get_user_ckpt_msr(struct task_struct *task) { return task->thread.ckpt_regs.msr | task->thread.fpexc_mode; }

Contributors

PersonTokensPropCommitsCommitProp
Anshuman Khandual27100.00%1100.00%
Total27100.00%1100.00%


static int set_user_ckpt_msr(struct task_struct *task, unsigned long msr) { task->thread.ckpt_regs.msr &= ~MSR_DEBUGCHANGE; task->thread.ckpt_regs.msr |= msr & MSR_DEBUGCHANGE; return 0; }

Contributors

PersonTokensPropCommitsCommitProp
Anshuman Khandual41100.00%1100.00%
Total41100.00%1100.00%


static int set_user_ckpt_trap(struct task_struct *task, unsigned long trap) { task->thread.ckpt_regs.trap = trap & 0xfff0; return 0; }

Contributors

PersonTokensPropCommitsCommitProp
Anshuman Khandual30100.00%1100.00%
Total30100.00%1100.00%

#endif #ifdef CONFIG_PPC64
static int get_user_dscr(struct task_struct *task, unsigned long *data) { *data = task->thread.dscr; return 0; }

Contributors

PersonTokensPropCommitsCommitProp
Alexey Kardashevskiy28100.00%2100.00%
Total28100.00%2100.00%


static int set_user_dscr(struct task_struct *task, unsigned long dscr) { task->thread.dscr = dscr; task->thread.dscr_inherit = 1; return 0; }

Contributors

PersonTokensPropCommitsCommitProp
Alexey Kardashevskiy34100.00%1100.00%
Total34100.00%1100.00%

#else
static int get_user_dscr(struct task_struct *task, unsigned long *data) { return -EIO; }

Contributors

PersonTokensPropCommitsCommitProp
Alexey Kardashevskiy20100.00%2100.00%
Total20100.00%2100.00%


static int set_user_dscr(struct task_struct *task, unsigned long dscr) { return -EIO; }

Contributors

PersonTokensPropCommitsCommitProp
Alexey Kardashevskiy19100.00%1100.00%
Total19100.00%1100.00%

#endif /* * We prevent mucking around with the reserved area of trap * which are used internally by the kernel. */
static int set_user_trap(struct task_struct *task, unsigned long trap) { task->thread.regs->trap = trap & 0xfff0; return 0; }

Contributors

PersonTokensPropCommitsCommitProp
Roland McGrath30100.00%1100.00%
Total30100.00%1100.00%

/* * Get contents of register REGNO in task TASK. */
int ptrace_get_reg(struct task_struct *task, int regno, unsigned long *data) { if ((task->thread.regs == NULL) || !data) return -EIO; if (regno == PT_MSR) { *data = get_user_msr(task); return 0; } if (regno == PT_DSCR) return get_user_dscr(task, data); if (regno < (sizeof(struct pt_regs) / sizeof(unsigned long))) { *data = ((unsigned long *)task->thread.regs)[regno]; return 0; } return -EIO; }

Contributors

PersonTokensPropCommitsCommitProp
Benjamin Herrenschmidt7262.07%125.00%
Alexey Kardashevskiy4135.34%250.00%
Roland McGrath32.59%125.00%
Total116100.00%4100.00%

/* * Write contents of register REGNO in task TASK. */
int ptrace_put_reg(struct task_struct *task, int regno, unsigned long data) { if (task->thread.regs == NULL) return -EIO; if (regno == PT_MSR) return set_user_msr(task, data); if (regno == PT_TRAP) return set_user_trap(task, data); if (regno == PT_DSCR) return set_user_dscr(task, data); if (regno <= PT_MAX_PUT_REG) { ((unsigned long *)task->thread.regs)[regno] = data; return 0; } return -EIO; }

Contributors

PersonTokensPropCommitsCommitProp
Benjamin Herrenschmidt7267.92%250.00%
Roland McGrath2018.87%125.00%
Alexey Kardashevskiy1413.21%125.00%
Total106100.00%4100.00%


static int gpr_get(struct task_struct *target, const struct user_regset *regset, unsigned int pos, unsigned int count, void *kbuf, void __user *ubuf) { int i, ret; if (target->thread.regs == NULL) return -EIO; if (!FULL_REGS(target->thread.regs)) { /* We have a partial register set. Fill 14-31 with bogus values */ for (i = 14; i < 32; i++) target->thread.regs->gpr[i] = NV_REG_POISON; } ret = user_regset_copyout(&pos, &count, &kbuf, &ubuf, target->thread.regs, 0, offsetof(struct pt_regs, msr)); if (!ret) { unsigned long msr = get_user_msr(target); ret = user_regset_copyout(&pos, &count, &kbuf, &ubuf, &msr, offsetof(struct pt_regs, msr), offsetof(struct pt_regs, msr) + sizeof(msr)); } BUILD_BUG_ON(offsetof(struct pt_regs, orig_gpr3) != offsetof(struct pt_regs, msr) + sizeof(long)); if (!ret) ret = user_regset_copyout(&pos, &count, &kbuf, &ubuf, &target->thread.regs->orig_gpr3, offsetof(struct pt_regs, orig_gpr3), sizeof(struct pt_regs)); if (!ret) ret = user_regset_copyout_zero(&pos, &count, &kbuf, &ubuf, sizeof(struct pt_regs), -1); return ret; }

Contributors

PersonTokensPropCommitsCommitProp
Roland McGrath25287.80%150.00%
Michael Wolf3512.20%150.00%
Total287100.00%2100.00%


static int gpr_set(struct task_struct *target, const struct user_regset *regset, unsigned int pos, unsigned int count, const void *kbuf, const void __user *ubuf) { unsigned long reg; int ret; if (target->thread.regs == NULL) return -EIO; CHECK_FULL_REGS(target->thread.regs); ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf, target->thread.regs, 0, PT_MSR * sizeof(reg)); if (!ret && count > 0) { ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf, &reg, PT_MSR * sizeof(reg), (PT_MSR + 1) * sizeof(reg)); if (!ret) ret = set_user_msr(target, reg); } BUILD_BUG_ON(offsetof(struct pt_regs, orig_gpr3) != offsetof(struct pt_regs, msr) + sizeof(long)); if (!ret) ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf, &target->thread.regs->orig_gpr3, PT_ORIG_R3 * sizeof(reg), (PT_MAX_PUT_REG + 1) * sizeof(reg)); if (PT_MAX_PUT_REG + 1 < PT_TRAP && !ret) ret = user_regset_copyin_ignore( &pos, &count, &kbuf, &ubuf, (PT_MAX_PUT_REG + 1) * sizeof(reg), PT_TRAP * sizeof(reg)); if (!ret && count > 0) { ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf, &reg, PT_TRAP * sizeof(reg), (PT_TRAP + 1) * sizeof(reg)); if (!ret) ret = set_user_trap(target, reg); } if (!ret) ret = user_regset_copyin_ignore( &pos, &count, &kbuf, &ubuf, (PT_TRAP + 1) * sizeof(reg), -1); return ret; }

Contributors

PersonTokensPropCommitsCommitProp
Roland McGrath382100.00%1100.00%
Total382100.00%1100.00%

/* * Regardless of transactions, 'fp_state' holds the current running * value of all FPR registers and 'ckfp_state' holds the last checkpointed * value of all FPR registers for the current transaction. * * Userspace interface buffer layout: * * struct data { * u64 fpr[32]; * u64 fpscr; * }; */
static int fpr_get(struct task_struct *target, const struct user_regset *regset, unsigned int pos, unsigned int count, void *kbuf, void __user *ubuf) { #ifdef CONFIG_VSX u64 buf[33]; int i; flush_fp_to_thread(target); /* copy to local buffer then write that out */ for (i = 0; i < 32 ; i++) buf[i] = target->thread.TS_FPR(i); buf[32] = target->thread.fp_state.fpscr; return user_regset_copyout(&pos, &count, &kbuf, &ubuf, buf, 0, -1); #else BUILD_BUG_ON(offsetof(struct thread_fp_state, fpscr) != offsetof(struct thread_fp_state, fpr[32])); flush_fp_to_thread(target); return user_regset_copyout(&pos, &count, &kbuf, &ubuf, &target->thread.fp_state, 0, -1); #endif }

Contributors

PersonTokensPropCommitsCommitProp
Roland McGrath8347.70%125.00%
Michael Neuling7442.53%125.00%
Paul Mackerras105.75%125.00%
Cyril Bur74.02%125.00%
Total174100.00%4100.00%

/* * Regardless of transactions, 'fp_state' holds the current running * value of all FPR registers and 'ckfp_state' holds the last checkpointed * value of all FPR registers for the current transaction. * * Userspace interface buffer layout: * * struct data { * u64 fpr[32]; * u64 fpscr; * }; * */
static int fpr_set(struct task_struct *target, const struct user_regset *regset, unsigned int pos, unsigned int count, const void *kbuf, const void __user *ubuf) { #ifdef CONFIG_VSX u64 buf[33]; int i; flush_fp_to_thread(target); for (i = 0; i < 32 ; i++) buf[i] = target->thread.TS_FPR(i); buf[32] = target->thread.fp_state.fpscr; /* copy to local buffer then write that out */ i = user_regset_copyin(&pos, &count, &kbuf, &ubuf, buf, 0, -1); if (i) return i; for (i = 0; i < 32 ; i++) target->thread.TS_FPR(i) = buf[i]; target->thread.fp_state.fpscr = buf[32]; return 0; #else BUILD_BUG_ON(offsetof(struct thread_fp_state, fpscr) != offsetof(struct thread_fp_state, fpr[32])); flush_fp_to_thread(target); return user_regset_copyin(&pos, &count, &kbuf, &ubuf, &target->thread.fp_state, 0, -1); #endif }

Contributors

PersonTokensPropCommitsCommitProp
Michael Neuling8537.44%120.00%
Roland McGrath8537.44%120.00%
Dave P Martin4017.62%120.00%
Paul Mackerras104.41%120.00%
Cyril Bur73.08%120.00%
Total227100.00%5100.00%

#ifdef CONFIG_ALTIVEC /* * Get/set all the altivec registers vr0..vr31, vscr, vrsave, in one go. * The transfer totals 34 quadword. Quadwords 0-31 contain the * corresponding vector registers. Quadword 32 contains the vscr as the * last word (offset 12) within that quadword. Quadword 33 contains the * vrsave as the first word (offset 0) within the quadword. * * This definition of the VMX state is compatible with the current PPC32 * ptrace interface. This allows signal handling and ptrace to use the * same structures. This also simplifies the implementation of a bi-arch * (combined (32- and 64-bit) gdb. */
static int vr_active(struct task_struct *target, const struct user_regset *regset) { flush_altivec_to_thread(target); return target->thread.used_vr ? regset->n : 0; }

Contributors

PersonTokensPropCommitsCommitProp
Benjamin Herrenschmidt2057.14%150.00%
Roland McGrath1542.86%150.00%
Total35100.00%2100.00%

/* * Regardless of transactions, 'vr_state' holds the current running * value of all the VMX registers and 'ckvr_state' holds the last * checkpointed value of all the VMX registers for the current * transaction to fall back on in case it aborts. * * Userspace interface buffer layout: * * struct data { * vector128 vr[32]; * vector128 vscr; * vector128 vrsave; * }; */
static int vr_get(struct task_struct *target, const struct user_regset *regset, unsigned int pos, unsigned int count, void *kbuf, void __user *ubuf) { int ret; flush_altivec_to_thread(target); BUILD_BUG_ON(offsetof(struct thread_vr_state, vscr) != offsetof(struct thread_vr_state, vr[32])); ret = user_regset_copyout(&pos, &count, &kbuf, &ubuf, &target->thread.vr_state, 0, 33 * sizeof(vector128)); if (!ret) { /* * Copy out only the low-order word of vrsave. */ union { elf_vrreg_t reg; u32 word; } vrsave; memset(&vrsave, 0, sizeof(vrsave)); vrsave.word = target->thread.vrsave; ret = user_regset_copyout(&pos, &count, &kbuf, &ubuf, &vrsave, 33 * sizeof(vector128), -1); } return ret; }

Contributors

PersonTokensPropCommitsCommitProp
Roland McGrath12673.26%120.00%
Benjamin Herrenschmidt3520.35%120.00%
Cyril Bur84.65%120.00%
Paul Mackerras21.16%120.00%
Anshuman Khandual10.58%120.00%
Total172100.00%5100.00%

/* * Regardless of transactions, 'vr_state' holds the current running * value of all the VMX registers and 'ckvr_state' holds the last * checkpointed value of all the VMX registers for the current * transaction to fall back on in case it aborts. * * Userspace interface buffer layout: * * struct data { * vector128 vr[32]; * vector128 vscr; * vector128 vrsave; * }; */
static int vr_set(struct task_struct *target, const struct user_regset *regset, unsigned int pos, unsigned int count, const void *kbuf, const void __user *ubuf) { int ret; flush_altivec_to_thread(target); BUILD_BUG_ON(offsetof(struct thread_vr_state, vscr) != offsetof(struct thread_vr_state, vr[32])); ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf, &target->thread.vr_state, 0, 33 * sizeof(vector128)); if (!ret && count > 0) { /* * We use only the first word of vrsave. */ union { elf_vrreg_t reg; u32 word; } vrsave; memset(&vrsave, 0, sizeof(vrsave)); vrsave.word = target->thread.vrsave; ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf, &vrsave, 33 * sizeof(vector128), -1); if (!ret) target->thread.vrsave = vrsave.word; } return ret; }

Contributors

PersonTokensPropCommitsCommitProp
Roland McGrath16987.56%120.00%
Anshuman Khandual105.18%120.00%
Benjamin Herrenschmidt63.11%120.00%
Cyril Bur63.11%120.00%
Paul Mackerras21.04%120.00%
Total193100.00%5100.00%

#endif /* CONFIG_ALTIVEC */ #ifdef CONFIG_VSX /* * Currently to set and and get all the vsx state, you need to call * the fp and VMX calls as well. This only get/sets the lower 32 * 128bit VSX registers. */
static int vsr_active(struct task_struct *target, const struct user_regset *regset) { flush_vsx_to_thread(target); return target->thread.used_vsr ? regset->n : 0; }

Contributors

PersonTokensPropCommitsCommitProp
Michael Neuling35100.00%1100.00%
Total35100.00%1100.00%

/* * Regardless of transactions, 'fp_state' holds the current running * value of all FPR registers and 'ckfp_state' holds the last * checkpointed value of all FPR registers for the current * transaction. * * Userspace interface buffer layout: * * struct data { * u64 vsx[32]; * }; */
static int vsr_get(struct task_struct *target, const struct user_regset *regset, unsigned int pos, unsigned int count, void *kbuf, void __user *ubuf) { u64 buf[32]; int ret, i; flush_tmregs_to_thread(target); flush_fp_to_thread(target); flush_altivec_to_thread(target); flush_vsx_to_thread(target); for (i = 0; i < 32 ; i++) buf[i] = target->thread.fp_state.fpr[i][TS_VSRLOWOFFSET]; ret = user_regset_copyout(&pos, &count, &kbuf, &ubuf, buf, 0, 32 * sizeof(double)); return ret; }

Contributors

PersonTokensPropCommitsCommitProp
Michael Neuling8163.28%350.00%
Cyril Bur3930.47%116.67%
Anshuman Khandual64.69%116.67%
Paul Mackerras21.56%116.67%
Total128100.00%6100.00%

/* * Regardless of transactions, 'fp_state' holds the current running * value of all FPR registers and 'ckfp_state' holds the last * checkpointed value of all FPR registers for the current * transaction. * * Userspace interface buffer layout: * * struct data { * u64 vsx[32]; * }; */
static int vsr_set(struct task_struct *target, const struct user_regset *regset, unsigned int pos, unsigned int count, const void *kbuf, const void __user *ubuf) { u64 buf[32]; int ret,i; flush_tmregs_to_thread(target); flush_fp_to_thread(target); flush_altivec_to_thread(target); flush_vsx_to_thread(target); for (i = 0; i < 32 ; i++) buf[i] = target->thread.fp_state.fpr[i][TS_VSRLOWOFFSET]; ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf, buf, 0, 32 * sizeof(double)); if (!ret) for (i = 0; i < 32 ; i++) target->thread.fp_state.fpr[i][TS_VSRLOWOFFSET] = buf[i]; return ret; }

Contributors

PersonTokensPropCommitsCommitProp
Michael Neuling11267.07%342.86%
Dave P Martin3219.16%114.29%
Anshuman Khandual158.98%114.29%
Cyril Bur52.99%114.29%
Paul Mackerras31.80%114.29%
Total167100.00%7100.00%

#endif