android_kernel_modules_leno.../debug_tools/vtunedk/src/pmi.c

520 lines
22 KiB
C

/*COPYRIGHT**
Copyright (C) 2005-2014 Intel Corporation. All Rights Reserved.
This file is part of SEP Development Kit
SEP Development Kit is free software; you can redistribute it
and/or modify it under the terms of the GNU General Public License
version 2 as published by the Free Software Foundation.
SEP Development Kit is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with SEP Development Kit; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
As a special exception, you may use this file as part of a free software
library without restriction. Specifically, if other files instantiate
templates or use macros or inline functions from this file, or you compile
this file and link it with other files to produce an executable, this
file does not by itself cause the resulting executable to be covered by
the GNU General Public License. This exception does not however
invalidate any other reasons why the executable file might be covered by
the GNU General Public License.
**COPYRIGHT*/
#include "lwpmudrv_defines.h"
#include <linux/version.h>
#include <linux/kernel.h>
#include <linux/ptrace.h>
#if defined(DRV_EM64T)
#include <asm/desc.h>
#endif
#include "lwpmudrv_types.h"
#include "rise_errors.h"
#include "lwpmudrv_ecb.h"
#include "lwpmudrv_struct.h"
#if defined(DRV_IA32) || defined(DRV_EM64T)
#include "apic.h"
#endif
#include "lwpmudrv.h"
#include "output.h"
#include "control.h"
#include "pmi.h"
#include "utility.h"
#if defined(DRV_IA32) || defined(DRV_EM64T)
#include "pebs.h"
#endif
#include "lwpmudrv_chipset.h"
#if defined(DRV_IA32) || defined(DRV_EM64T)
#include "sepdrv_p_state.h"
#endif
// Desc id #0 is used for module records
#define COMPUTE_DESC_ID(index) ((index))
extern DRV_CONFIG pcfg;
extern uid_t uid;
#define EFLAGS_V86_MASK 0x00020000L
/*********************************************************************
* Global Variables / State
*********************************************************************/
/*********************************************************************
* Interrupt Handler
*********************************************************************/
/*
* PMI_Interrupt_Handler
* Arguments
* IntFrame - Pointer to the Interrupt Frame
*
* Returns
* None
*
* Description
* Grab the data that is needed to populate the sample records
*/
#if defined(DRV_IA32)
asmlinkage VOID
PMI_Interrupt_Handler (
struct pt_regs *regs
)
{
SampleRecordPC *psamp;
CPU_STATE pcpu;
BUFFER_DESC bd;
U32 csdlo; // low half code seg descriptor
U32 csdhi; // high half code seg descriptor
U32 seg_cs; // code seg selector
DRV_MASKS_NODE event_mask;
U32 this_cpu;
U32 i;
U32 pid;
U32 tid;
U64 tsc;
U32 desc_id;
EVENT_DESC evt_desc;
#if defined(SECURE_SEP)
uid_t l_uid;
#endif
U32 accept_interrupt = 1;
U32 dev_idx;
U32 event_idx;
DRV_CONFIG pcfg_unc;
DISPATCH dispatch_unc;
U64 *result_buffer;
U64 diff;
U64 lbr_tos_from_ip = 0;
this_cpu = CONTROL_THIS_CPU();
pcpu = &pcb[this_cpu];
bd = &cpu_buf[this_cpu];
SYS_Locked_Inc(&CPU_STATE_in_interrupt(pcpu));
// Disable the counter control
dispatch->freeze(NULL);
#if defined(SECURE_SEP)
l_uid = DRV_GET_UID(current);
accept_interrupt = (l_uid == uid);
#endif
dispatch->check_overflow(&event_mask);
if (GLOBAL_STATE_current_phase(driver_state) == DRV_STATE_RUNNING &&
CPU_STATE_accept_interrupt(&pcb[this_cpu])) {
pid = GET_CURRENT_TGID();
tid = current->pid;
if (DRV_CONFIG_target_pid(pcfg) > 0 && pid != DRV_CONFIG_target_pid(pcfg)) {
accept_interrupt = 0;
}
if (accept_interrupt) {
UTILITY_Read_TSC(&tsc);
for (i = 0; i < event_mask.masks_num; i++) {
desc_id = COMPUTE_DESC_ID(DRV_EVENT_MASK_event_idx(&event_mask.eventmasks[i]));
evt_desc = desc_data[desc_id];
psamp = (SampleRecordPC *)OUTPUT_Reserve_Buffer_Space(bd,
EVENT_DESC_sample_size(evt_desc));
if (!psamp) {
continue;
}
CPU_STATE_num_samples(pcpu) += 1;
SAMPLE_RECORD_descriptor_id(psamp) = desc_id;
SAMPLE_RECORD_tsc(psamp) = tsc;
SAMPLE_RECORD_pid_rec_index_raw(psamp) = 1;
SAMPLE_RECORD_pid_rec_index(psamp) = pid;
SAMPLE_RECORD_tid(psamp) = tid;
SAMPLE_RECORD_eip(psamp) = REGS_eip(regs);
SAMPLE_RECORD_eflags(psamp) = REGS_eflags(regs);
SAMPLE_RECORD_cpu_num(psamp) = (U16) this_cpu;
SAMPLE_RECORD_cs(psamp) = (U16) REGS_xcs(regs);
if (SAMPLE_RECORD_eflags(psamp) & EFLAGS_V86_MASK) {
csdlo = 0;
csdhi = 0;
}
else {
seg_cs = SAMPLE_RECORD_cs(psamp);
SYS_Get_CSD(seg_cs, &csdlo, &csdhi);
}
SAMPLE_RECORD_csd(psamp).u1.lowWord = csdlo;
SAMPLE_RECORD_csd(psamp).u2.highWord = csdhi;
SEP_PRINT_DEBUG("SAMPLE_RECORD_pid_rec_index(psamp) %x\n", SAMPLE_RECORD_pid_rec_index(psamp));
SEP_PRINT_DEBUG("SAMPLE_RECORD_tid(psamp) %x\n", SAMPLE_RECORD_tid(psamp));
SEP_PRINT_DEBUG("SAMPLE_RECORD_eip(psamp) %x\n", SAMPLE_RECORD_eip(psamp));
SEP_PRINT_DEBUG("SAMPLE_RECORD_eflags(psamp) %x\n", SAMPLE_RECORD_eflags(psamp));
SEP_PRINT_DEBUG("SAMPLE_RECORD_cpu_num(psamp) %x\n", SAMPLE_RECORD_cpu_num(psamp));
SEP_PRINT_DEBUG("SAMPLE_RECORD_cs(psamp) %x\n", SAMPLE_RECORD_cs(psamp));
SEP_PRINT_DEBUG("SAMPLE_RECORD_csd(psamp).lowWord %x\n", SAMPLE_RECORD_csd(psamp).u1.lowWord);
SEP_PRINT_DEBUG("SAMPLE_RECORD_csd(psamp).highWord %x\n", SAMPLE_RECORD_csd(psamp).u2.highWord);
SAMPLE_RECORD_event_index(psamp) = DRV_EVENT_MASK_event_idx(&event_mask.eventmasks[i]);
if (DRV_EVENT_MASK_precise(&event_mask.eventmasks[i]) == 1) {
if (EVENT_DESC_pebs_offset(evt_desc) ||
EVENT_DESC_latency_offset_in_sample(evt_desc)) {
PEBS_Fill_Buffer((S8 *)psamp,
evt_desc,
DRV_CONFIG_virt_phys_translation(pcfg));
}
PEBS_Modify_IP((S8 *)psamp, FALSE);
PEBS_Modify_TSC((S8 *)psamp);
}
if (DRV_CONFIG_collect_lbrs(pcfg) && (DRV_EVENT_MASK_lbr_capture(&event_mask.eventmasks[i]))) {
lbr_tos_from_ip = dispatch->read_lbrs(!DRV_CONFIG_store_lbrs(pcfg) ? NULL:((S8 *)(psamp)+EVENT_DESC_lbr_offset(evt_desc)));
if (DRV_EVENT_MASK_branch(&event_mask.eventmasks[i]) && DRV_CONFIG_precise_ip_lbrs(pcfg) && lbr_tos_from_ip) {
SAMPLE_RECORD_eip(psamp) = (U32) lbr_tos_from_ip;
SEP_PRINT_DEBUG("UPDATED SAMPLE_RECORD_eip(psamp) %x\n", SAMPLE_RECORD_eip(psamp));
}
}
if (DRV_CONFIG_power_capture(pcfg)) {
dispatch->read_power(((S8 *)(psamp)+EVENT_DESC_power_offset_in_sample(evt_desc)));
}
if (DRV_CONFIG_enable_chipset(pcfg)) {
cs_dispatch->read_counters(((S8 *)(psamp)+DRV_CONFIG_chipset_offset(pcfg)));
}
if (DRV_CONFIG_event_based_counts(pcfg)) {
dispatch->read_counts((S8 *)psamp, DRV_EVENT_MASK_event_idx(&event_mask.eventmasks[i]));
}
#if (defined(DRV_IA32) || defined(DRV_EM64T))
if (DRV_CONFIG_enable_p_state(pcfg)) {
SEPDRV_P_STATE_Read((S8 *)(psamp)+EVENT_DESC_p_state_offset(evt_desc), pcpu);
if (!DRV_CONFIG_event_based_counts(pcfg) && CPU_STATE_p_state_counting(pcpu)) {
dispatch->read_counts((S8 *)psamp, DRV_EVENT_MASK_event_idx(&event_mask.eventmasks[i]));
}
}
#endif
// need to do this per device
for (dev_idx = 0; dev_idx < num_devices; dev_idx++) {
pcfg_unc = LWPMU_DEVICE_pcfg(&devices[dev_idx]);
dispatch_unc = LWPMU_DEVICE_dispatch(&devices[dev_idx]);
if (pcfg_unc && DRV_CONFIG_event_based_counts(pcfg_unc)) {
dispatch_unc->read_counts((S8 *)(psamp), dev_idx);
SAMPLE_RECORD_uncore_valid(psamp) = 1;
// skip first element because it's the group number
result_buffer = (U64*) ((S8*)(psamp) + DRV_CONFIG_results_offset(pcfg_unc));
for (event_idx = 1;
event_idx < LWPMU_DEVICE_num_events(&devices[dev_idx]) + 1; // need to account for 1st element being group id
event_idx++) {
// check for overflow
if (result_buffer[event_idx] < LWPMU_DEVICE_prev_val_per_thread(&devices[dev_idx])[this_cpu][event_idx]) {
// overflow occurred! need to compensate
diff = LWPMU_DEVICE_counter_mask(&devices[dev_idx])
- LWPMU_DEVICE_prev_val_per_thread(&devices[dev_idx])[this_cpu][event_idx];
diff += result_buffer[event_idx];
}
else {
diff = result_buffer[event_idx] - LWPMU_DEVICE_prev_val_per_thread(&devices[dev_idx])[this_cpu][event_idx];
}
// accumulate current results into accumulator
LWPMU_DEVICE_acc_per_thread(&devices[dev_idx])[this_cpu][event_idx] += diff;
// update previous results array
LWPMU_DEVICE_prev_val_per_thread(&devices[dev_idx])[this_cpu][event_idx] = result_buffer[event_idx];
// update results buffer for this thread
result_buffer[event_idx] = LWPMU_DEVICE_acc_per_thread(&devices[dev_idx])[this_cpu][event_idx];
}
}
}
} // for
}
}
if (DRV_CONFIG_pebs_mode(pcfg)) {
PEBS_Reset_Index(this_cpu);
}
APIC_Ack_Eoi();
// Reset the data counters
if (CPU_STATE_trigger_count(&pcb[this_cpu]) == 0) {
dispatch->swap_group(FALSE);
}
// Re-enable the counter control
dispatch->restart(NULL);
atomic_set(&CPU_STATE_in_interrupt(&pcb[this_cpu]), 0);
return;
}
#endif // DRV_IA32
#if defined(DRV_EM64T)
#define IS_LDT_BIT 0x4
#define SEGMENT_SHIFT 3
IDTGDT_DESC gdt_desc;
static U32
pmi_Get_CSD (
U32 seg,
U32 *low,
U32 *high
)
{
PVOID gdt_max_addr;
struct desc_struct *gdt;
CodeDescriptor *csd;
//
// These could be pre-init'ed values
//
gdt_max_addr = (PVOID) (((U64) gdt_desc.idtgdt_base) + gdt_desc.idtgdt_limit);
gdt = gdt_desc.idtgdt_base;
//
// end pre-init potential...
//
//
// We don't do ldt's
//
if (seg & IS_LDT_BIT) {
*low = 0;
*high = 0;
return (FALSE);
}
//
// segment offset is based on dropping the bottom 3 bits...
//
csd = (CodeDescriptor *) &(gdt[seg >> SEGMENT_SHIFT]);
if (((PVOID) csd) >= gdt_max_addr) {
SEP_PRINT_WARNING("segment too big in get_CSD(0x%x)\n", seg);
return FALSE;
}
*low = csd->u1.lowWord;
*high = csd->u2.highWord;
SEP_PRINT_DEBUG("get_CSD - seg 0x%x, low %08x, high %08x, reserved_0: %d\n",
seg, *low, *high, csd->u2.s2.reserved_0);
return TRUE;
}
asmlinkage VOID
PMI_Interrupt_Handler (
struct pt_regs *regs
)
{
SampleRecordPC *psamp;
CPU_STATE pcpu;
BUFFER_DESC bd;
DRV_MASKS_NODE event_mask;
U32 this_cpu;
U32 i;
U32 is_64bit_addr;
U32 pid;
U32 tid;
U64 tsc;
U32 desc_id;
EVENT_DESC evt_desc;
U32 accept_interrupt = 1;
#if defined(SECURE_SEP)
uid_t l_uid;
#endif
DRV_CONFIG pcfg_unc;
DISPATCH dispatch_unc;
U32 dev_idx;
U32 event_idx;
U64 *result_buffer;
U64 diff;
U64 lbr_tos_from_ip = 0;
// Disable the counter control
dispatch->freeze(NULL);
this_cpu = CONTROL_THIS_CPU();
pcpu = &pcb[this_cpu];
bd = &cpu_buf[this_cpu];
SYS_Locked_Inc(&CPU_STATE_in_interrupt(pcpu));
#if defined(SECURE_SEP)
l_uid = DRV_GET_UID(current);
accept_interrupt = (l_uid == uid);
#endif
dispatch->check_overflow(&event_mask);
if (GLOBAL_STATE_current_phase(driver_state) == DRV_STATE_RUNNING &&
CPU_STATE_accept_interrupt(&pcb[this_cpu])) {
pid = GET_CURRENT_TGID();
tid = current->pid;
if (DRV_CONFIG_target_pid(pcfg) > 0 && pid != DRV_CONFIG_target_pid(pcfg)) {
accept_interrupt = 0;
}
if (accept_interrupt) {
UTILITY_Read_TSC(&tsc);
for (i = 0; i < event_mask.masks_num; i++) {
desc_id = COMPUTE_DESC_ID(DRV_EVENT_MASK_event_idx(&event_mask.eventmasks[i]));
evt_desc = desc_data[desc_id];
psamp = (SampleRecordPC *)OUTPUT_Reserve_Buffer_Space(bd,
EVENT_DESC_sample_size(evt_desc));
if (!psamp) {
continue;
}
CPU_STATE_num_samples(pcpu) += 1;
SAMPLE_RECORD_descriptor_id(psamp) = desc_id;
SAMPLE_RECORD_tsc(psamp) = tsc;
SAMPLE_RECORD_pid_rec_index_raw(psamp) = 1;
SAMPLE_RECORD_pid_rec_index(psamp) = pid;
SAMPLE_RECORD_tid(psamp) = tid;
SAMPLE_RECORD_cpu_num(psamp) = (U16) this_cpu;
SAMPLE_RECORD_cs(psamp) = (U16) REGS_cs(regs);
pmi_Get_CSD(SAMPLE_RECORD_cs(psamp),
&SAMPLE_RECORD_csd(psamp).u1.lowWord,
&SAMPLE_RECORD_csd(psamp).u2.highWord);
SEP_PRINT_DEBUG("SAMPLE_RECORD_pid_rec_index(psamp) %x\n", SAMPLE_RECORD_pid_rec_index(psamp));
SEP_PRINT_DEBUG("SAMPLE_RECORD_tid(psamp) %x\n", SAMPLE_RECORD_tid(psamp));
SEP_PRINT_DEBUG("SAMPLE_RECORD_cpu_num(psamp) %x\n", SAMPLE_RECORD_cpu_num(psamp));
SEP_PRINT_DEBUG("SAMPLE_RECORD_cs(psamp) %x\n", SAMPLE_RECORD_cs(psamp));
SEP_PRINT_DEBUG("SAMPLE_RECORD_csd(psamp).lowWord %x\n", SAMPLE_RECORD_csd(psamp).u1.lowWord);
SEP_PRINT_DEBUG("SAMPLE_RECORD_csd(psamp).highWord %x\n", SAMPLE_RECORD_csd(psamp).u2.highWord);
is_64bit_addr = (SAMPLE_RECORD_csd(psamp).u2.s2.reserved_0 == 1);
if (is_64bit_addr) {
SAMPLE_RECORD_iip(psamp) = REGS_rip(regs);
SAMPLE_RECORD_ipsr(psamp) = (REGS_eflags(regs) & 0xffffffff) |
(((U64) SAMPLE_RECORD_csd(psamp).u2.s2.dpl) << 32);
SAMPLE_RECORD_ia64_pc(psamp) = TRUE;
}
else {
SAMPLE_RECORD_eip(psamp) = REGS_rip(regs);
SAMPLE_RECORD_eflags(psamp) = REGS_eflags(regs);
SAMPLE_RECORD_ia64_pc(psamp) = FALSE;
SEP_PRINT_DEBUG("SAMPLE_RECORD_eip(psamp) 0x%x\n", SAMPLE_RECORD_eip(psamp));
SEP_PRINT_DEBUG("SAMPLE_RECORD_eflags(psamp) %x\n", SAMPLE_RECORD_eflags(psamp));
}
SAMPLE_RECORD_event_index(psamp) = DRV_EVENT_MASK_event_idx(&event_mask.eventmasks[i]);
if (DRV_EVENT_MASK_precise(&event_mask.eventmasks[i])) {
if ( EVENT_DESC_pebs_offset(evt_desc) ||
EVENT_DESC_latency_offset_in_sample(evt_desc)) {
PEBS_Fill_Buffer((S8 *)psamp,
evt_desc,
DRV_CONFIG_virt_phys_translation(pcfg));
}
PEBS_Modify_IP((S8 *)psamp, is_64bit_addr);
PEBS_Modify_TSC((S8 *)psamp);
}
if (DRV_CONFIG_collect_lbrs(pcfg) && (DRV_EVENT_MASK_lbr_capture(&event_mask.eventmasks[i]))) {
lbr_tos_from_ip = dispatch->read_lbrs(!DRV_CONFIG_store_lbrs(pcfg) ? NULL:((S8 *)(psamp)+EVENT_DESC_lbr_offset(evt_desc)));
if (DRV_EVENT_MASK_branch(&event_mask.eventmasks[i]) && DRV_CONFIG_precise_ip_lbrs(pcfg) && lbr_tos_from_ip) {
if (is_64bit_addr) {
SAMPLE_RECORD_iip(psamp) = lbr_tos_from_ip;
SEP_PRINT_DEBUG("UPDATED SAMPLE_RECORD_iip(psamp) 0x%llx\n", SAMPLE_RECORD_iip(psamp));
}
else {
SAMPLE_RECORD_eip(psamp) = (U32) lbr_tos_from_ip;
SEP_PRINT_DEBUG("UPDATED SAMPLE_RECORD_eip(psamp) 0x%x\n", SAMPLE_RECORD_eip(psamp));
}
}
}
if (DRV_CONFIG_power_capture(pcfg)) {
dispatch->read_power(((S8 *)(psamp)+EVENT_DESC_power_offset_in_sample(evt_desc)));
}
if (DRV_CONFIG_enable_chipset(pcfg)) {
cs_dispatch->read_counters(((S8 *)(psamp)+DRV_CONFIG_chipset_offset(pcfg)));
}
if (DRV_CONFIG_event_based_counts(pcfg)) {
dispatch->read_counts((S8 *)psamp, DRV_EVENT_MASK_event_idx(&event_mask.eventmasks[i]));
}
#if (defined(DRV_IA32) || defined(DRV_EM64T))
if (DRV_CONFIG_enable_p_state(pcfg)) {
SEPDRV_P_STATE_Read((S8 *)(psamp)+EVENT_DESC_p_state_offset(evt_desc), pcpu);
if (!DRV_CONFIG_event_based_counts(pcfg) && CPU_STATE_p_state_counting(pcpu)) {
dispatch->read_counts((S8 *) psamp, DRV_EVENT_MASK_event_idx(&event_mask.eventmasks[i]));
}
}
#endif
for (dev_idx = 0; dev_idx < num_devices; dev_idx++) {
pcfg_unc = LWPMU_DEVICE_pcfg(&devices[dev_idx]);
dispatch_unc = LWPMU_DEVICE_dispatch(&devices[dev_idx]);
if (pcfg_unc && DRV_CONFIG_event_based_counts(pcfg_unc)) {
dispatch_unc->read_counts((S8 *)(psamp), dev_idx);
SAMPLE_RECORD_uncore_valid(psamp) = 1;
// skip first element because it's the group number
result_buffer = (U64*) ((S8*)(psamp) + DRV_CONFIG_results_offset(pcfg_unc));
for (event_idx = 1;
event_idx < LWPMU_DEVICE_num_events(&devices[dev_idx]) + 1; // need to account for 1st element being group id
event_idx++) {
// check for overflow
if (result_buffer[event_idx] < LWPMU_DEVICE_prev_val_per_thread(&devices[dev_idx])[this_cpu][event_idx]) {
// overflow occurred! need to compensate
diff = LWPMU_DEVICE_counter_mask(&devices[dev_idx])
- LWPMU_DEVICE_prev_val_per_thread(&devices[dev_idx])[this_cpu][event_idx];
diff += result_buffer[event_idx];
}
else {
diff = result_buffer[event_idx] - LWPMU_DEVICE_prev_val_per_thread(&devices[dev_idx])[this_cpu][event_idx];
}
// accumulate current results into accumulator
LWPMU_DEVICE_acc_per_thread(&devices[dev_idx])[this_cpu][event_idx] += diff;
// update previous results array
LWPMU_DEVICE_prev_val_per_thread(&devices[dev_idx])[this_cpu][event_idx] = result_buffer[event_idx];
// update results buffer for this thread
result_buffer[event_idx] = LWPMU_DEVICE_acc_per_thread(&devices[dev_idx])[this_cpu][event_idx];
}
}
}
}
}
}
if (DRV_CONFIG_pebs_mode(pcfg)) {
PEBS_Reset_Index(this_cpu);
}
APIC_Ack_Eoi();
// Reset the data counters
if (CPU_STATE_trigger_count(&pcb[this_cpu]) == 0) {
dispatch->swap_group(FALSE);
}
// Re-enable the counter control
dispatch->restart(NULL);
atomic_set(&CPU_STATE_in_interrupt(&pcb[this_cpu]), 0);
return;
}
#endif