android_kernel_modules_leno.../debug_tools/vtunedk/src/linuxos.c

608 lines
18 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/module.h>
#include <linux/notifier.h>
#include <linux/profile.h>
#include <linux/sched.h>
#include <linux/mm.h>
#include <linux/sched.h>
#include <linux/fs.h>
#include "lwpmudrv_types.h"
#include "rise_errors.h"
#include "lwpmudrv_ecb.h"
#include "lwpmudrv_struct.h"
#include "inc/lwpmudrv.h"
#include "lwpmudrv_ioctl.h"
#include "inc/control.h"
#include "inc/utility.h"
#include "inc/output.h"
#include "inc/linuxos.h"
extern uid_t uid;
extern volatile pid_t control_pid;
extern volatile S32 abnormal_terminate;
static volatile S32 hooks_installed = 0;
extern int
LWPMUDRV_Abnormal_Terminate(void);
#define MY_TASK PROFILE_TASK_EXIT
#define MY_UNMAP PROFILE_MUNMAP
#if defined(DRV_IA32)
static U16
linuxos_Get_Exec_Mode (
struct task_struct *p
)
{
return ((unsigned short) MODE_32BIT);
}
#endif
#if defined(DRV_EM64T)
static U16
linuxos_Get_Exec_Mode (
struct task_struct *p
)
{
if (!p) {
return MODE_UNKNOWN;
}
if (test_tsk_thread_flag(p,TIF_IA32)) {
return ((unsigned short) MODE_32BIT);
}
return ((unsigned short) MODE_64BIT);
}
#endif
static S32
linuxos_Load_Image_Notify_Routine (
char *name,
PVOID base,
U32 size,
U32 pid,
U32 parent_pid,
U32 options,
unsigned short mode,
S32 load_event
)
{
char *raw_path;
ModuleRecord *mra;
char buf[sizeof(ModuleRecord) + MAXNAMELEN + 32];
U64 tsc_read;
S32 local_load_event = (load_event==-1) ? 0 : load_event;
mra = (ModuleRecord *) buf;
memset(mra, '\0', sizeof(buf));
raw_path = (char*) mra + sizeof(ModuleRecord);
MODULE_RECORD_processed(mra) = 0;
MODULE_RECORD_segment_type(mra) = mode;
MODULE_RECORD_load_addr64(mra) = (U64)(size_t)base;
MODULE_RECORD_length64(mra) = size;
MODULE_RECORD_segment_number(mra) = 1; // for user modules
MODULE_RECORD_global_module_tb5(mra) = options & LOPTS_GLOBAL_MODULE;
MODULE_RECORD_first_module_rec_in_process(mra) = options & LOPTS_1ST_MODREC;
MODULE_RECORD_tsc_used(mra) = 1;
MODULE_RECORD_exe(mra) = 0;
MODULE_RECORD_parent_pid(mra) = parent_pid;
UTILITY_Read_TSC(&tsc_read);
preempt_disable();
tsc_read -= TSC_SKEW(CONTROL_THIS_CPU());
preempt_enable();
if (local_load_event) {
MR_unloadTscSet(mra, tsc_read);
}
else {
MR_unloadTscSet(mra, (U64)(-1));
}
MODULE_RECORD_pid_rec_index(mra) = pid;
MODULE_RECORD_pid_rec_index_raw(mra) = 1; // raw pid
#if defined(DEBUG)
if (total_loads_init) {
SEP_PRINT_DEBUG("samp_load_image_notify: setting pid_rec_index_raw pid 0x%x %s \n",
pid, name);
}
#endif
strncpy(raw_path, name, MAXNAMELEN);
raw_path[MAXNAMELEN] = 0;
MODULE_RECORD_path_length(mra) = (U16) strlen(raw_path) + 1;
MODULE_RECORD_rec_length(mra) = (U16) ALIGN_8(sizeof (ModuleRecord) +
MODULE_RECORD_path_length(mra));
#if defined(DRV_IA32)
MODULE_RECORD_selector(mra) = (pid==0) ? __KERNEL_CS : __USER_CS;
#endif
#if defined(DRV_EM64T)
if (mode == MODE_64BIT) {
MODULE_RECORD_selector(mra) = (pid==0) ? __KERNEL_CS : __USER_CS;
}
else if (mode == MODE_32BIT) {
MODULE_RECORD_selector(mra) = (pid==0) ? __KERNEL32_CS : __USER32_CS;
}
#endif
if (LOPTS_EXE & options) {
MODULE_RECORD_exe(mra) = 1;
}
OUTPUT_Module_Fill((PVOID)mra, MODULE_RECORD_rec_length(mra));
return OS_SUCCESS;
}
#ifdef DRV_MM_EXE_FILE_PRESENT
static DRV_BOOL
linuxos_Equal_VM_Exe_File (
struct vm_area_struct *vma
)
{
S8 name_vm_file[MAXNAMELEN];
S8 name_exe_file[MAXNAMELEN];
S8 *pname_vm_file = NULL;
S8 *pname_exe_file = NULL;
if (vma == NULL) {
return FALSE;
}
if (vma->vm_file == NULL) {
return FALSE;
}
if (vma->vm_mm->exe_file == NULL) {
return FALSE;
}
pname_vm_file = D_PATH(vma->vm_file, name_vm_file, MAXNAMELEN);
pname_exe_file = D_PATH(vma->vm_mm->exe_file, name_exe_file, MAXNAMELEN);
return (strcmp (pname_vm_file, pname_exe_file) == 0);
}
#endif
//
// Register the module for a process. The task_struct and mm
// should be locked if necessary to make sure they don't change while we're
// iterating...
// Used as a service routine
//
static S32
linuxos_VMA_For_Process (
struct task_struct *p,
struct vm_area_struct *vma,
S32 load_event,
U32 *first
)
{
U32 options = 0;
S8 name[MAXNAMELEN];
S8 *pname = NULL;
U32 ppid = 0;
U16 exec_mode;
#if defined(DRV_ANDROID)
char andr_app[MAXNAMELEN +1];
#endif
if (p == NULL) {
SEP_PRINT_ERROR("linuxos_VMA_For_Process skipped p=NULL\n")
return OS_SUCCESS;
}
if (vma->vm_file) pname = D_PATH(vma->vm_file, name, MAXNAMELEN);
if (!IS_ERR(pname) && pname != NULL) {
SEP_PRINT_DEBUG("enum: %s, %d, %lx, %lx \n",
pname, p->pid, vma->vm_start, (vma->vm_end - vma->vm_start));
// if the VM_EXECUTABLE flag is set then this is the module
// that is being used to name the module
if (DRV_VM_MOD_EXECUTABLE(vma)) {
options |= LOPTS_EXE;
#if defined(DRV_ANDROID)
if(!strcmp (pname, "/system/bin/app_process")){
memset(andr_app, '\0', MAXNAMELEN + 1);
strncpy(andr_app, p->comm, MAXNAMELEN);
pname = andr_app;
}
#endif
}
// mark the first of the bunch...
if (*first == 1) {
options |= LOPTS_1ST_MODREC;
*first = 0;
}
}
#if defined(DRV_IA32) || defined(DRV_EM64T)
#if defined(DRV_ALLOW_VDSO)
else if (vma->vm_mm &&
vma->vm_start == (long)vma->vm_mm->context.vdso) {
pname = "[vdso]";
}
#endif
#if defined(DRV_ALLOW_SYSCALL)
else if (vma->vm_start == VSYSCALL_START) {
pname = "[vsyscall]";
}
#endif
#endif
if (pname != NULL) {
options = 0;
if (DRV_VM_MOD_EXECUTABLE(vma)) {
options |= LOPTS_EXE;
}
if (p && p->parent) {
ppid = p->parent->tgid;
}
exec_mode = linuxos_Get_Exec_Mode(p);
// record this module
linuxos_Load_Image_Notify_Routine(pname,
(PVOID)vma->vm_start,
(vma->vm_end - vma->vm_start),
p->pid,
ppid,
options,
exec_mode,
load_event);
}
return OS_SUCCESS;
}
//
// Common loop to enumerate all modules for a process. The task_struct and mm
// should be locked if necessary to make sure they don't change while we're
// iterating...
//
static S32
linuxos_Enum_Modules_For_Process (
struct task_struct *p,
struct mm_struct *mm,
S32 load_event
)
{
struct vm_area_struct *mmap;
U32 first = 1;
#if defined(SECURE_SEP)
uid_t l_uid;
l_uid = DRV_GET_UID(p);
/*
* Check for: same uid, or root uid
*/
if (l_uid != uid && l_uid != 0) {
return OS_SUCCESS;
}
#endif
for (mmap = mm->mmap; mmap; mmap = mmap->vm_next) {
/* We have 3 distinct conditions here.
* 1) Is the page executable?
* 2) Is is a part of the vdso area?
* 3) Is it the vsyscall area?
*/
if (((mmap->vm_flags & VM_EXEC) &&
mmap->vm_file &&
mmap->vm_file->f_dentry)
#if defined(DRV_IA32) || defined(DRV_EM64T)
#if defined(DRV_ALLOW_VDSO)
||
(mmap->vm_mm &&
mmap->vm_start == (long)mmap->vm_mm->context.vdso)
#endif
#endif
#if defined(DRV_ALLOW_VSYSCALL)
||
(mmap->vm_start == VSYSCALL_START)
#endif
) {
linuxos_VMA_For_Process(p,
mmap,
load_event,
&first);
}
}
return OS_SUCCESS;
}
/* ------------------------------------------------------------------------- */
/*!
* @fn static int linuxos_Exec_Unmap_Notify(
* struct notifier_block *self,
* unsigned long val,
* VOID *data)
*
* @brief this function is called whenever a task exits
*
* @param self IN - not used
* val IN - not used
* data IN - this is cast in the mm_struct of the task that is call unmap
*
* @return none
*
* <I>Special Notes:</I>
*
* This notification is called from do_munmap(mm/mmap.c). This is called when ever
* a module is loaded or unloaded. It looks like it is called right after a module is
* loaded or before its unloaded (if using dlopen,dlclose).
* However it is not called when a process is exiting instead exit_mmap is called
* (resulting in an EXIT_MMAP notification).
*/
static int
linuxos_Exec_Unmap_Notify (
struct notifier_block *self,
unsigned long val,
PVOID data
)
{
struct mm_struct *mm;
struct vm_area_struct *mmap = NULL;
U32 first = 1;
#if defined(SECURE_SEP)
uid_t l_uid;
l_uid = DRV_GET_UID(current);
/*
* Check for: same uid, or root uid
*/
if (l_uid != uid && l_uid != 0) {
return 0;
}
#endif
if (GLOBAL_STATE_current_phase(driver_state) == DRV_STATE_UNINITIALIZED) {
return 0;
}
mm = current->mm;
down_read(&mm->mmap_sem);
mmap = FIND_VMA (mm, data);
if (mmap &&
mmap->vm_file &&
(mmap->vm_flags & VM_EXEC)) {
linuxos_VMA_For_Process(current, mmap, TRUE, &first);
}
up_read(&mm->mmap_sem);
return 0;
}
/* ------------------------------------------------------------------------- */
/*!
* @fn OS_STATUS LINUXOS_Enum_Process_Modules(DRV_BOOL at_end)
*
* @brief gather all the process modules that are present.
*
* @param at_end - the collection happens at the end of the sampling run
*
* @return OS_SUCCESS
*
* <I>Special Notes:</I>
* This routine gathers all the process modules that are present
* in the system at this time. If at_end is set to be TRUE, then
* act as if all the modules are being unloaded.
*
*/
extern OS_STATUS
LINUXOS_Enum_Process_Modules (
DRV_BOOL at_end
)
{
int n = 0;
struct task_struct *p;
SEP_PRINT_DEBUG("Enum_Process_Modules begin tasks\n");
if (abnormal_terminate == 1) {
return OS_SUCCESS;
}
FOR_EACH_TASK(p) {
SEP_PRINT_DEBUG("Enum_Process_Modules looking at task %d\n", n);
/*
* Call driver notification routine for each module
* that is mapped into the process created by the fork
*/
if (p == NULL) {
SEP_PRINT_DEBUG("Enum_Process_Modules skipped p=NULL\n");
continue;
}
if (p->mm == NULL) {
SEP_PRINT_DEBUG("Enum_Process_Modules skipped p=0x%p (pid=%d), p->mm=NULL, p->comm=%s\n", p, p->pid, p->comm);
if (p->comm) {
linuxos_Load_Image_Notify_Routine(p->comm,
NULL,
0,
p->pid,
(p->parent) ? p->parent->tgid : 0,
LOPTS_EXE | LOPTS_1ST_MODREC,
linuxos_Get_Exec_Mode(p),
1);
}
continue;
}
if (!UTILITY_down_read_mm(p)) {
SEP_PRINT_ERROR("Linux_Enum_Process_Modules_End: unable to get lock on mmap_sem!\n");
return OS_SUCCESS;
}
linuxos_Enum_Modules_For_Process(p, p->mm, at_end?-1:0);
UTILITY_up_read_mm(p);
n++;
}
SEP_PRINT_DEBUG("Enum_Process_Modules done with %d tasks\n", n);
return OS_SUCCESS;
}
/* ------------------------------------------------------------------------- */
/*!
* @fn static int linuxos_Exit_Task_Notify(struct notifier_block * self,
* unsigned long val, PVOID data)
* @brief this function is called whenever a task exits
*
* @param self IN - not used
* val IN - not used
* data IN - this is cast into the task_struct of the exiting task
*
* @return none
*
* <I>Special Notes:</I>
* this function is called whenever a task exits. It is called right before
* the virtual memory areas are freed. We just enumerate through all the modules
* of the task and set the unload sample count and the load event flag to 1 to
* indicate this is a module unload
*/
static int
linuxos_Exit_Task_Notify (
struct notifier_block *self,
unsigned long val,
PVOID data
)
{
struct task_struct *p = (struct task_struct *)data;
int status = OS_SUCCESS;
if (GLOBAL_STATE_current_phase(driver_state) == DRV_STATE_UNINITIALIZED) {
return status;
}
if (!p->mm) {
return status;
}
SEP_PRINT_DEBUG("exit_task_notify pid = %d tgid = %d\n", p->pid, p->tgid);
if (p->pid == control_pid) {
SEP_PRINT_DEBUG("The collector task has been terminated via an uncatchable signal\n");
if (GLOBAL_STATE_current_phase(driver_state) == DRV_STATE_PREPARE_STOP ||
GLOBAL_STATE_current_phase(driver_state) == DRV_STATE_STOPPED) {
return status;
}
status = LWPMUDRV_Abnormal_Terminate();
}
else if (abnormal_terminate == 0) {
linuxos_Enum_Modules_For_Process(p, p->mm, 1);
}
return status;
}
/*
* The notifier block. All the static entries have been defined at this point
*/
static struct notifier_block linuxos_exec_unmap_nb = {
.notifier_call = linuxos_Exec_Unmap_Notify,
};
static struct notifier_block linuxos_exit_task_nb = {
.notifier_call = linuxos_Exit_Task_Notify,
};
/* ------------------------------------------------------------------------- */
/*!
* @fn VOID LINUXOS_Install_Hooks(VOID)
* @brief registers the profiling callbacks
*
* @param none
*
* @return none
*
* <I>Special Notes:</I>
*
* None
*/
extern VOID
LINUXOS_Install_Hooks (
VOID
)
{
int err = 0;
int err2 = 0;
if (hooks_installed == 1) {
SEP_PRINT_DEBUG("The OS Hooks are already installed\n");
return;
}
err = profile_event_register(MY_UNMAP, &linuxos_exec_unmap_nb);
err2= profile_event_register(MY_TASK, &linuxos_exit_task_nb);
if (err || err2) {
if (err == OS_NO_SYSCALL) {
SEP_PRINT_WARNING("This kernel does not implement kernel profiling hooks. "
"Task termination and image unloads will not be tracked "
"during sampling session!!\n");
}
}
hooks_installed = 1;
return;
}
/* ------------------------------------------------------------------------- */
/*!
* @fn VOID LINUXOS_Uninstall_Hooks(VOID)
* @brief unregisters the profiling callbacks
*
* @param none
*
* @return
*
* <I>Special Notes:</I>
*
* None
*/
extern VOID
LINUXOS_Uninstall_Hooks (
VOID
)
{
if (hooks_installed == 0) {
return;
}
SEP_PRINT_DEBUG("Uninstalling OS Hooks\n");
hooks_installed = 0;
profile_event_unregister(MY_UNMAP, &linuxos_exec_unmap_nb);
profile_event_unregister(MY_TASK, &linuxos_exit_task_nb);
return;
}