diff --git a/arch/x86/entry/entry_64.S b/arch/x86/entry/entry_64.S
index 748d85cdc336..06d794043d8e 100644
--- a/arch/x86/entry/entry_64.S
+++ b/arch/x86/entry/entry_64.S
@@ -53,6 +53,15 @@ ENTRY(native_usergs_sysret64)
 END(native_usergs_sysret64)
 #endif /* CONFIG_PARAVIRT */
 
+.macro TRACE_IRQS_IRETQ_NOTRACE
+#ifdef CONFIG_TRACE_IRQFLAGS
+	bt      $9, EFLAGS(%rsp)		/* interrupts off? */
+	jnc     1f
+	TRACE_IRQS_ON_NOTRACE
+1:
+#endif
+.endm
+
 .macro TRACE_IRQS_IRETQ
 #ifdef CONFIG_TRACE_IRQFLAGS
 	bt	$9, EFLAGS(%rsp)		/* interrupts off? */
@@ -62,6 +71,13 @@ END(native_usergs_sysret64)
 #endif
 .endm
 
+.macro TRACE_IRQS_COND_OFF
+	bt      $9, EFLAGS(%rsp)		/* interrupts off? */
+	jnc     1f
+	TRACE_IRQS_OFF
+1:
+.endm
+
 /*
  * When dynamic function tracer is enabled it will add a breakpoint
  * to all locations that it is about to modify, sync CPUs, update
@@ -94,10 +110,31 @@ END(native_usergs_sysret64)
 1:
 .endm
 
+.macro TRACE_IRQS_OFF_DEBUG_NOTRACE
+	call    debug_stack_set_zero
+	TRACE_IRQS_OFF_NOTRACE
+	call    debug_stack_reset
+.endm
+
+.macro TRACE_IRQS_ON_DEBUG_NOTRACE
+	call    debug_stack_set_zero
+	TRACE_IRQS_ON_NOTRACE
+	call    debug_stack_reset
+.endm
+
+.macro TRACE_IRQS_IRETQ_DEBUG_NOTRACE
+	bt      $9, EFLAGS(%rsp)		/* interrupts off? */
+	jnc     1f
+	TRACE_IRQS_ON_DEBUG_NOTRACE
+1:
+.endm
 #else
-# define TRACE_IRQS_OFF_DEBUG			TRACE_IRQS_OFF
-# define TRACE_IRQS_ON_DEBUG			TRACE_IRQS_ON
-# define TRACE_IRQS_IRETQ_DEBUG			TRACE_IRQS_IRETQ
+# define TRACE_IRQS_OFF_DEBUG				TRACE_IRQS_OFF
+# define TRACE_IRQS_ON_DEBUG				TRACE_IRQS_ON
+# define TRACE_IRQS_IRETQ_DEBUG				TRACE_IRQS_IRETQ
+# define TRACE_IRQS_OFF_DEBUG_NOTRACE			TRACE_IRQS_OFF_NOTRACE
+# define TRACE_IRQS_ON_DEBUG_NOTRACE			TRACE_IRQS_ON_NOTRACE
+# define TRACE_IRQS_IRETQ_DEBUG_NOTRACE			TRACE_IRQS_IRETQ_NOTRACE
 #endif
 
 /*
@@ -685,7 +722,7 @@ END(irq_entries_start)
 	 * we enter from user mode.  There's no reason to optimize this since
 	 * TRACE_IRQS_OFF is a no-op if lockdep is off.
 	 */
-	TRACE_IRQS_OFF
+	TRACE_IRQS_OFF_NOTRACE
 
 	CALL_enter_from_user_mode
 
@@ -709,7 +746,7 @@ common_interrupt:
 	/* 0(%rsp): old RSP */
 ret_from_intr:
 	DISABLE_INTERRUPTS(CLBR_ANY)
-	TRACE_IRQS_OFF
+	TRACE_IRQS_COND_OFF
 
 	LEAVE_IRQ_STACK
 
@@ -1013,7 +1050,7 @@ ENTRY(switch_to_thread_stack)
 	ret
 END(switch_to_thread_stack)
 
-.macro idtentry sym do_sym has_error_code:req paranoid=0 shift_ist=-1
+.macro idtentry sym do_sym has_error_code:req paranoid=0 shift_ist=-1 donottrace=0
 ENTRY(\sym)
 	UNWIND_HINT_IRET_REGS offset=\has_error_code*8
 
@@ -1044,11 +1081,19 @@ ENTRY(\sym)
 	/* returned flag: ebx=0: need swapgs on exit, ebx=1: don't need it */
 
 	.if \paranoid
+	.if \donottrace != 1
 	.if \shift_ist != -1
 	TRACE_IRQS_OFF_DEBUG			/* reload IDT in case of recursion */
 	.else
 	TRACE_IRQS_OFF
 	.endif
+	.else
+	.if \shift_ist != -1
+	TRACE_IRQS_OFF_DEBUG_NOTRACE            /* reload IDT in case of recursion */
+	.else
+	TRACE_IRQS_OFF_NOTRACE
+	.endif
+	.endif
 	.endif
 
 	movq	%rsp, %rdi			/* pt_regs pointer */
@@ -1259,7 +1304,7 @@ apicinterrupt3 HYPERVISOR_CALLBACK_VECTOR \
 #endif /* CONFIG_HYPERV */
 
 idtentry debug			do_debug		has_error_code=0	paranoid=1 shift_ist=DEBUG_STACK
-idtentry int3			do_int3			has_error_code=0	paranoid=1 shift_ist=DEBUG_STACK
+idtentry int3			do_int3			has_error_code=0	paranoid=1 shift_ist=DEBUG_STACK donottrace=1
 idtentry stack_segment		do_stack_segment	has_error_code=1
 
 #ifdef CONFIG_XEN
@@ -1319,15 +1364,15 @@ END(paranoid_entry)
 ENTRY(paranoid_exit)
 	UNWIND_HINT_REGS
 	DISABLE_INTERRUPTS(CLBR_ANY)
-	TRACE_IRQS_OFF_DEBUG
+	TRACE_IRQS_OFF_DEBUG_NOTRACE
 	testl	%ebx, %ebx			/* swapgs needed? */
 	jnz	.Lparanoid_exit_no_swapgs
-	TRACE_IRQS_IRETQ
+	TRACE_IRQS_IRETQ_NOTRACE
 	RESTORE_CR3	scratch_reg=%rbx save_reg=%r14
 	SWAPGS_UNSAFE_STACK
 	jmp	.Lparanoid_exit_restore
 .Lparanoid_exit_no_swapgs:
-	TRACE_IRQS_IRETQ_DEBUG
+	TRACE_IRQS_IRETQ_DEBUG_NOTRACE
 .Lparanoid_exit_restore:
 	jmp restore_regs_and_return_to_kernel
 END(paranoid_exit)
diff --git a/arch/x86/entry/thunk_64.S b/arch/x86/entry/thunk_64.S
index be36bf4e0957..e4f5609e73b9 100644
--- a/arch/x86/entry/thunk_64.S
+++ b/arch/x86/entry/thunk_64.S
@@ -41,6 +41,8 @@
 #ifdef CONFIG_TRACE_IRQFLAGS
 	THUNK trace_hardirqs_on_thunk,trace_hardirqs_on_caller,1
 	THUNK trace_hardirqs_off_thunk,trace_hardirqs_off_caller,1
+	THUNK trace_hardirqs_on_thunk_notrace,trace_hardirqs_on_caller_notrace,1
+	THUNK trace_hardirqs_off_thunk_notrace,trace_hardirqs_off_caller_notrace,1
 #endif
 
 #ifdef CONFIG_DEBUG_LOCK_ALLOC
diff --git a/arch/x86/include/asm/hardirq.h b/arch/x86/include/asm/hardirq.h
index 51cc979dd364..b23264ba19d2 100644
--- a/arch/x86/include/asm/hardirq.h
+++ b/arch/x86/include/asm/hardirq.h
@@ -5,6 +5,8 @@
 #include <linux/threads.h>
 #include <linux/irq.h>
 
+#define __ARCH_IRQ_EXIT_IRQS_DISABLED  1
+
 typedef struct {
 	unsigned int __softirq_pending;
 	unsigned int __nmi_count;	/* arch dependent */
diff --git a/arch/x86/include/asm/irqflags.h b/arch/x86/include/asm/irqflags.h
index 89f08955fff7..5a1b0df3ae4b 100644
--- a/arch/x86/include/asm/irqflags.h
+++ b/arch/x86/include/asm/irqflags.h
@@ -171,11 +171,16 @@ static inline int arch_irqs_disabled(void)
 
 #ifdef __ASSEMBLY__
 #ifdef CONFIG_TRACE_IRQFLAGS
-#  define TRACE_IRQS_ON		call trace_hardirqs_on_thunk;
-#  define TRACE_IRQS_OFF	call trace_hardirqs_off_thunk;
+#  define TRACE_IRQS_ON				call trace_hardirqs_on_thunk;
+#  define TRACE_IRQS_OFF			call trace_hardirqs_off_thunk;
+#  define TRACE_IRQS_ON_NOTRACE			call trace_hardirqs_on_thunk_notrace;
+#  define TRACE_IRQS_OFF_NOTRACE		call trace_hardirqs_off_thunk_notrace;
 #else
 #  define TRACE_IRQS_ON
 #  define TRACE_IRQS_OFF
+#  define TRACE_IRQS_ON_NOTRACE
+#  define TRACE_IRQS_OFF_NOTRACE
+#  define TRACE_IRQS_COND_OFF
 #endif
 #ifdef CONFIG_DEBUG_LOCK_ALLOC
 #  ifdef CONFIG_X86_64
diff --git a/arch/x86/include/asm/trace/irq_vectors.h b/arch/x86/include/asm/trace/irq_vectors.h
index 8eb139ed1a03..1d518066adbb 100644
--- a/arch/x86/include/asm/trace/irq_vectors.h
+++ b/arch/x86/include/asm/trace/irq_vectors.h
@@ -138,6 +138,23 @@ DEFINE_IRQ_VECTOR_EVENT(deferred_error_apic);
 DEFINE_IRQ_VECTOR_EVENT(thermal_apic);
 #endif
 
+/*
+ * user_defined -called when entering/exiting from regular device interrupt
+ * vector handler.
+ */
+DEFINE_IRQ_VECTOR_EVENT(user_defined);
+
+/*
+ * move_cleanup -called when entering/exiting from move_cleanup interrupt
+ * vector handler.
+ */
+DEFINE_IRQ_VECTOR_EVENT(move_cleanup);
+
+/*
+ * nmi -called when entering/exiting from nmi vector handler.
+ */
+DEFINE_IRQ_VECTOR_EVENT(nmi);
+
 #endif /* CONFIG_X86_LOCAL_APIC */
 
 #undef TRACE_INCLUDE_PATH
diff --git a/arch/x86/kernel/apic/apic.c b/arch/x86/kernel/apic/apic.c
index 89c7c8569e5e..06a361e4b67d 100644
--- a/arch/x86/kernel/apic/apic.c
+++ b/arch/x86/kernel/apic/apic.c
@@ -1032,6 +1032,9 @@ static void local_apic_timer_interrupt(void)
 	evt->event_handler(evt);
 }
 
+void __irq_enter_early_tp(void);
+void __irq_exit_late_tp(void);
+
 /*
  * Local APIC timer interrupt. This is the most natural way for doing
  * local interrupts, but local timer interrupts can be emulated by
@@ -1052,13 +1055,15 @@ __visible void __irq_entry smp_apic_timer_interrupt(struct pt_regs *regs)
 	 * Besides, if we don't timer interrupts ignore the global
 	 * interrupt lock, which is the WrongThing (tm) to do.
 	 */
-	entering_ack_irq();
+	__irq_enter_early_tp();
 	trace_local_timer_entry(LOCAL_TIMER_VECTOR);
+	entering_ack_irq();
 	local_apic_timer_interrupt();
-	trace_local_timer_exit(LOCAL_TIMER_VECTOR);
 	exiting_irq();
 
 	set_irq_regs(old_regs);
+	trace_local_timer_exit(LOCAL_TIMER_VECTOR);
+	__irq_exit_late_tp();
 }
 
 int setup_profiling_timer(unsigned int multiplier)
@@ -1919,8 +1924,9 @@ __visible void __irq_entry smp_spurious_interrupt(struct pt_regs *regs)
 	u8 vector = ~regs->orig_ax;
 	u32 v;
 
-	entering_irq();
+	__irq_enter_early_tp();
 	trace_spurious_apic_entry(vector);
+	entering_irq();
 
 	/*
 	 * Check if this really is a spurious interrupt and ACK it
@@ -1937,8 +1943,9 @@ __visible void __irq_entry smp_spurious_interrupt(struct pt_regs *regs)
 	pr_info("spurious APIC interrupt through vector %02x on CPU#%d, "
 		"should never happen.\n", vector, smp_processor_id());
 
-	trace_spurious_apic_exit(vector);
 	exiting_irq();
+	trace_spurious_apic_exit(vector);
+	__irq_exit_late_tp();
 }
 
 /*
@@ -1958,8 +1965,10 @@ __visible void __irq_entry smp_error_interrupt(struct pt_regs *regs)
 	};
 	u32 v, i = 0;
 
-	entering_irq();
+	__irq_enter_early_tp();
 	trace_error_apic_entry(ERROR_APIC_VECTOR);
+	entering_irq();
+
 
 	/* First tickle the hardware, only then report what went on. -- REW */
 	if (lapic_get_maxlvt() > 3)	/* Due to the Pentium erratum 3AP. */
@@ -1981,8 +1990,9 @@ __visible void __irq_entry smp_error_interrupt(struct pt_regs *regs)
 
 	apic_printk(APIC_DEBUG, KERN_CONT "\n");
 
-	trace_error_apic_exit(ERROR_APIC_VECTOR);
 	exiting_irq();
+	trace_error_apic_exit(ERROR_APIC_VECTOR);
+	__irq_exit_late_tp();
 }
 
 /**
diff --git a/arch/x86/kernel/apic/vector.c b/arch/x86/kernel/apic/vector.c
index 2ce1c708b8ee..8664fe82c272 100644
--- a/arch/x86/kernel/apic/vector.c
+++ b/arch/x86/kernel/apic/vector.c
@@ -21,6 +21,8 @@
 #include <asm/desc.h>
 #include <asm/irq_remapping.h>
 
+#include <asm/trace/irq_vectors.h>
+
 struct apic_chip_data {
 	struct irq_cfg		cfg;
 	cpumask_var_t		domain;
@@ -583,10 +585,14 @@ void send_cleanup_vector(struct irq_cfg *cfg)
 		__send_cleanup_vector(data);
 }
 
+void __irq_enter_early_tp(void);
+void __irq_exit_late_tp(void);
 asmlinkage __visible void __irq_entry smp_irq_move_cleanup_interrupt(void)
 {
 	unsigned vector, me;
 
+	 __irq_enter_early_tp();
+	trace_move_cleanup_entry(IRQ_MOVE_CLEANUP_VECTOR);
 	entering_ack_irq();
 
 	/* Prevent vectors vanishing under us */
@@ -659,6 +665,8 @@ asmlinkage __visible void __irq_entry smp_irq_move_cleanup_interrupt(void)
 	raw_spin_unlock(&vector_lock);
 
 	exiting_irq();
+	trace_move_cleanup_exit(IRQ_MOVE_CLEANUP_VECTOR);
+	__irq_exit_late_tp();
 }
 
 static void __irq_complete_move(struct irq_cfg *cfg, unsigned vector)
diff --git a/arch/x86/kernel/cpu/mcheck/mce_amd.c b/arch/x86/kernel/cpu/mcheck/mce_amd.c
index 486f640b02ef..1bf7b698a704 100644
--- a/arch/x86/kernel/cpu/mcheck/mce_amd.c
+++ b/arch/x86/kernel/cpu/mcheck/mce_amd.c
@@ -773,14 +773,18 @@ static void __log_error(unsigned int bank, u64 status, u64 addr, u64 misc)
 	mce_log(&m);
 }
 
+void __irq_enter_early_tp(void);
+void __irq_exit_late_tp(void);
 asmlinkage __visible void __irq_entry smp_deferred_error_interrupt(void)
 {
-	entering_irq();
+	__irq_enter_early_tp();
 	trace_deferred_error_apic_entry(DEFERRED_ERROR_VECTOR);
+	entering_irq();
 	inc_irq_stat(irq_deferred_error_count);
 	deferred_error_int_vector();
-	trace_deferred_error_apic_exit(DEFERRED_ERROR_VECTOR);
 	exiting_ack_irq();
+	trace_deferred_error_apic_exit(DEFERRED_ERROR_VECTOR);
+	__irq_exit_late_tp();
 }
 
 /*
diff --git a/arch/x86/kernel/cpu/mcheck/therm_throt.c b/arch/x86/kernel/cpu/mcheck/therm_throt.c
index 2da67b70ba98..133f011b9a65 100644
--- a/arch/x86/kernel/cpu/mcheck/therm_throt.c
+++ b/arch/x86/kernel/cpu/mcheck/therm_throt.c
@@ -390,14 +390,18 @@ static void unexpected_thermal_interrupt(void)
 
 static void (*smp_thermal_vector)(void) = unexpected_thermal_interrupt;
 
+void __irq_enter_early_tp(void);
+void __irq_exit_late_tp(void);
 asmlinkage __visible void __irq_entry smp_thermal_interrupt(struct pt_regs *r)
 {
-	entering_irq();
+	__irq_enter_early_tp();
 	trace_thermal_apic_entry(THERMAL_APIC_VECTOR);
+	entering_irq();
 	inc_irq_stat(irq_thermal_count);
 	smp_thermal_vector();
-	trace_thermal_apic_exit(THERMAL_APIC_VECTOR);
 	exiting_ack_irq();
+	trace_thermal_apic_exit(THERMAL_APIC_VECTOR);
+	__irq_exit_late_tp();
 }
 
 /* Thermal monitoring depends on APIC, ACPI and clock modulation */
diff --git a/arch/x86/kernel/cpu/mcheck/threshold.c b/arch/x86/kernel/cpu/mcheck/threshold.c
index 2b584b319eff..7cd564a89c9a 100644
--- a/arch/x86/kernel/cpu/mcheck/threshold.c
+++ b/arch/x86/kernel/cpu/mcheck/threshold.c
@@ -18,12 +18,16 @@ static void default_threshold_interrupt(void)
 
 void (*mce_threshold_vector)(void) = default_threshold_interrupt;
 
+void __irq_enter_early_tp(void);
+void __irq_exit_late_tp(void);
 asmlinkage __visible void __irq_entry smp_threshold_interrupt(void)
 {
-	entering_irq();
+	__irq_enter_early_tp();
 	trace_threshold_apic_entry(THRESHOLD_APIC_VECTOR);
+	entering_irq();
 	inc_irq_stat(irq_threshold_count);
 	mce_threshold_vector();
-	trace_threshold_apic_exit(THRESHOLD_APIC_VECTOR);
 	exiting_ack_irq();
+	trace_threshold_apic_exit(THRESHOLD_APIC_VECTOR);
+	__irq_exit_late_tp();
 }
diff --git a/arch/x86/kernel/irq.c b/arch/x86/kernel/irq.c
index aa9d51eea9d0..60552bf3036e 100644
--- a/arch/x86/kernel/irq.c
+++ b/arch/x86/kernel/irq.c
@@ -206,7 +206,8 @@ u64 arch_irq_stat(void)
 	return sum;
 }
 
-
+void __irq_enter_early_tp(void);
+void __irq_exit_late_tp(void);
 /*
  * do_IRQ handles all normal device IRQ's (the special
  * SMP cross-CPU interrupts have their own specific
@@ -219,6 +220,8 @@ __visible unsigned int __irq_entry do_IRQ(struct pt_regs *regs)
 	/* high bit used in ret_from_ code  */
 	unsigned vector = ~regs->orig_ax;
 
+	__irq_enter_early_tp();
+	trace_user_defined_entry(vector);
 	entering_irq();
 
 	/* entering_irq() tells RCU that we're not quiescent.  Check it. */
@@ -241,6 +244,8 @@ __visible unsigned int __irq_entry do_IRQ(struct pt_regs *regs)
 	exiting_irq();
 
 	set_irq_regs(old_regs);
+	trace_user_defined_exit(vector);
+	__irq_exit_late_tp();
 	return 1;
 }
 
@@ -254,13 +259,16 @@ __visible void __irq_entry smp_x86_platform_ipi(struct pt_regs *regs)
 {
 	struct pt_regs *old_regs = set_irq_regs(regs);
 
-	entering_ack_irq();
+	__irq_enter_early_tp();
 	trace_x86_platform_ipi_entry(X86_PLATFORM_IPI_VECTOR);
+	entering_ack_irq();
 	inc_irq_stat(x86_platform_ipis);
 	if (x86_platform_ipi_callback)
 		x86_platform_ipi_callback();
-	trace_x86_platform_ipi_exit(X86_PLATFORM_IPI_VECTOR);
+
 	exiting_irq();
+	trace_x86_platform_ipi_exit(X86_PLATFORM_IPI_VECTOR);
+	__irq_exit_late_tp();
 	set_irq_regs(old_regs);
 }
 #endif
@@ -430,6 +438,7 @@ void fixup_irqs(void)
 
 	irq_migrate_all_off_this_cpu();
 
+
 	/*
 	 * We can remove mdelay() and then send spuriuous interrupts to
 	 * new cpu targets for all the irqs that were handled previously by
diff --git a/arch/x86/kernel/irq_work.c b/arch/x86/kernel/irq_work.c
index 80bee7695a20..7b03c19a950d 100644
--- a/arch/x86/kernel/irq_work.c
+++ b/arch/x86/kernel/irq_work.c
@@ -12,15 +12,20 @@
 #include <asm/trace/irq_vectors.h>
 #include <linux/interrupt.h>
 
+
 #ifdef CONFIG_X86_LOCAL_APIC
+void __irq_enter_early_tp(void);
+void __irq_exit_late_tp(void);
 __visible void __irq_entry smp_irq_work_interrupt(struct pt_regs *regs)
 {
-	ipi_entering_ack_irq();
+	__irq_enter_early_tp();
 	trace_irq_work_entry(IRQ_WORK_VECTOR);
+	ipi_entering_ack_irq();
 	inc_irq_stat(apic_irq_work_irqs);
 	irq_work_run();
-	trace_irq_work_exit(IRQ_WORK_VECTOR);
 	exiting_irq();
+	trace_irq_work_exit(IRQ_WORK_VECTOR);
+	__irq_exit_late_tp();
 }
 
 void arch_irq_work_raise(void)
diff --git a/arch/x86/kernel/nmi.c b/arch/x86/kernel/nmi.c
index 35aafc95e4b8..88c0603ffb71 100644
--- a/arch/x86/kernel/nmi.c
+++ b/arch/x86/kernel/nmi.c
@@ -37,6 +37,8 @@
 
 #define CREATE_TRACE_POINTS
 #include <trace/events/nmi.h>
+#undef CREATE_TRACE_POINTS
+#include <asm/trace/irq_vectors.h>
 
 struct nmi_desc {
 	raw_spinlock_t lock;
@@ -514,12 +516,14 @@ do_nmi(struct pt_regs *regs, long error_code)
 #endif
 
 	nmi_enter();
+	trace_nmi_entry(2);
 
 	inc_irq_stat(__nmi_count);
 
 	if (!ignore_nmis)
 		default_do_nmi(regs);
 
+	trace_nmi_exit(2);
 	nmi_exit();
 
 #ifdef CONFIG_X86_64
diff --git a/arch/x86/kernel/smp.c b/arch/x86/kernel/smp.c
index 5c574dff4c1a..3f1a4a19e355 100644
--- a/arch/x86/kernel/smp.c
+++ b/arch/x86/kernel/smp.c
@@ -257,21 +257,25 @@ static void native_stop_other_cpus(int wait)
  * Reschedule call back. KVM uses this interrupt to force a cpu out of
  * guest mode
  */
+void __irq_enter_early_tp(void);
+void __irq_exit_late_tp(void);
 __visible void __irq_entry smp_reschedule_interrupt(struct pt_regs *regs)
 {
 	ack_APIC_irq();
 	inc_irq_stat(irq_resched_count);
-
 	if (trace_resched_ipi_enabled()) {
+
 		/*
 		 * scheduler_ipi() might call irq_enter() as well, but
 		 * nested calls are fine.
 		 */
-		irq_enter();
+		__irq_enter_early_tp();
 		trace_reschedule_entry(RESCHEDULE_VECTOR);
+		irq_enter();
 		scheduler_ipi();
-		trace_reschedule_exit(RESCHEDULE_VECTOR);
 		irq_exit();
+		trace_reschedule_exit(RESCHEDULE_VECTOR);
+		__irq_exit_late_tp();
 		return;
 	}
 	scheduler_ipi();
@@ -279,22 +283,26 @@ __visible void __irq_entry smp_reschedule_interrupt(struct pt_regs *regs)
 
 __visible void __irq_entry smp_call_function_interrupt(struct pt_regs *regs)
 {
-	ipi_entering_ack_irq();
+	__irq_enter_early_tp();
 	trace_call_function_entry(CALL_FUNCTION_VECTOR);
+	ipi_entering_ack_irq();
 	inc_irq_stat(irq_call_count);
 	generic_smp_call_function_interrupt();
-	trace_call_function_exit(CALL_FUNCTION_VECTOR);
 	exiting_irq();
+	trace_call_function_exit(CALL_FUNCTION_VECTOR);
+	__irq_exit_late_tp();
 }
 
 __visible void __irq_entry smp_call_function_single_interrupt(struct pt_regs *r)
 {
-	ipi_entering_ack_irq();
+	__irq_enter_early_tp();
 	trace_call_function_single_entry(CALL_FUNCTION_SINGLE_VECTOR);
+	ipi_entering_ack_irq();
 	inc_irq_stat(irq_call_count);
 	generic_smp_call_function_single_interrupt();
-	trace_call_function_single_exit(CALL_FUNCTION_SINGLE_VECTOR);
 	exiting_irq();
+	trace_call_function_single_exit(CALL_FUNCTION_SINGLE_VECTOR);
+	__irq_exit_late_tp();
 }
 
 static int __init nonmi_ipi_setup(char *str)
diff --git a/include/linux/irqflags.h b/include/linux/irqflags.h
index 2e023bfe45af..f7c4781a010a 100644
--- a/include/linux/irqflags.h
+++ b/include/linux/irqflags.h
@@ -110,23 +110,33 @@ do {						\
 #define local_irq_enable() \
 	do { trace_hardirqs_on(); raw_local_irq_enable(); } while (0)
 #define local_irq_disable() \
-	do { raw_local_irq_disable(); trace_hardirqs_off(); } while (0)
+	do {						\
+		int disabled = raw_irqs_disabled();     \
+		raw_local_irq_disable();		\
+		if (!disabled)				\
+			trace_hardirqs_off();		\
+	} while (0)
+
 #define local_irq_save(flags)				\
 	do {						\
+		int disabled = raw_irqs_disabled();	\
 		raw_local_irq_save(flags);		\
-		trace_hardirqs_off();			\
+		if (!disabled)				\
+			trace_hardirqs_off();		\
 	} while (0)
 
 
-#define local_irq_restore(flags)			\
-	do {						\
-		if (raw_irqs_disabled_flags(flags)) {	\
-			raw_local_irq_restore(flags);	\
-			trace_hardirqs_off();		\
-		} else {				\
-			trace_hardirqs_on();		\
-			raw_local_irq_restore(flags);	\
-		}					\
+#define local_irq_restore(flags)				\
+	do {							\
+		if (raw_irqs_disabled_flags(flags)) {		\
+			int disabled = raw_irqs_disabled();	\
+			raw_local_irq_restore(flags);		\
+			if (!disabled)				\
+				trace_hardirqs_off();		\
+		} else {					\
+			trace_hardirqs_on();			\
+			raw_local_irq_restore(flags);		\
+		}						\
 	} while (0)
 
 #define safe_halt()				\
diff --git a/include/linux/sched.h b/include/linux/sched.h
index c42f64997ccd..5fb327bb9fbb 100644
--- a/include/linux/sched.h
+++ b/include/linux/sched.h
@@ -107,23 +107,33 @@ struct task_group;
 					 (task->flags & PF_FROZEN) == 0 && \
 					 (task->state & TASK_NOLOAD) == 0)
 
+extern void trace_set_current_state(int state_value);
 #ifdef CONFIG_DEBUG_ATOMIC_SLEEP
 
 #define __set_current_state(state_value)			\
 	do {							\
+		trace_set_current_state(state_value);		\
 		current->task_state_change = _THIS_IP_;		\
 		current->state = (state_value);			\
 	} while (0)
 #define set_current_state(state_value)				\
 	do {							\
+		trace_set_current_state(state_value);		\
 		current->task_state_change = _THIS_IP_;		\
 		smp_store_mb(current->state, (state_value));	\
 	} while (0)
 
 #define __set_current_state_no_track(state_value)		\
-		current->state = (state_value);
+	do {							\
+		trace_set_current_state(state_value);		\
+		current->state = (state_value);			\
+	} while (0)
+
 #define set_current_state_no_track(state_value)			\
-		smp_store_mb(current->state, (state_value));
+	do {							\
+		trace_set_current_state(state_value);		\
+		smp_store_mb(current->state, (state_value));	\
+	} while (0)
 
 #else
 /*
@@ -160,8 +170,17 @@ struct task_group;
  *
  * Also see the comments of try_to_wake_up().
  */
-#define __set_current_state(state_value) do { current->state = (state_value); } while (0)
-#define set_current_state(state_value)	 smp_store_mb(current->state, (state_value))
+#define __set_current_state(state_value) 			\
+	do { 							\
+		trace_set_current_state(state_value);		\
+		current->state = (state_value);			\
+	} while (0)
+
+#define set_current_state(state_value)	 			\
+	do {							\
+		trace_set_current_state(state_value);		\
+		smp_store_mb(current->state, (state_value));	\
+	} while (0)
 
 #define __set_current_state_no_track(state_value)	__set_current_state(state_value)
 #define set_current_state_no_track(state_value)		set_current_state(state_value)
@@ -1585,10 +1604,7 @@ static inline int test_tsk_thread_flag(struct task_struct *tsk, int flag)
 	return test_ti_thread_flag(task_thread_info(tsk), flag);
 }
 
-static inline void set_tsk_need_resched(struct task_struct *tsk)
-{
-	set_tsk_thread_flag(tsk,TIF_NEED_RESCHED);
-}
+void set_tsk_need_resched(struct task_struct *tsk);
 
 static inline void clear_tsk_need_resched(struct task_struct *tsk)
 {
diff --git a/include/trace/events/irq.h b/include/trace/events/irq.h
index eeceafaaea4c..fddb2f8a22e0 100644
--- a/include/trace/events/irq.h
+++ b/include/trace/events/irq.h
@@ -160,6 +160,35 @@ DEFINE_EVENT(softirq, softirq_raise,
 	TP_ARGS(vec_nr)
 );
 
+DECLARE_EVENT_CLASS(irq_onoff,
+
+	TP_PROTO(unsigned long ip),
+
+	TP_ARGS(ip),
+
+	TP_STRUCT__entry(
+		__field(        unsigned long,       ip             )
+		),
+
+	TP_fast_assign(
+		__entry->ip = ip;
+	),
+
+	TP_printk(" at %pF", (void *) __entry->ip)
+);
+
+DEFINE_EVENT(irq_onoff, local_irq_enable,
+
+	TP_PROTO(unsigned long ip),
+
+	TP_ARGS(ip)
+);
+DEFINE_EVENT(irq_onoff, local_irq_disable,
+
+	TP_PROTO(unsigned long ip),
+
+	TP_ARGS(ip)
+);
 #endif /*  _TRACE_IRQ_H */
 
 /* This part must be outside protection */
diff --git a/include/trace/events/lock.h b/include/trace/events/lock.h
index d7512129a324..2bb80528ce18 100644
--- a/include/trace/events/lock.h
+++ b/include/trace/events/lock.h
@@ -81,6 +81,175 @@ DEFINE_EVENT(lock, lock_acquired,
 #endif
 #endif
 
+DECLARE_EVENT_CLASS(rt_mutex,
+
+	TP_PROTO(struct rt_mutex *lock, unsigned long ip),
+
+	TP_ARGS(lock, ip),
+
+	TP_STRUCT__entry(
+		__field(	void *,	lock		)
+		__field( unsigned long,	ip		)
+	),
+
+	TP_fast_assign(
+		__entry->lock		= (void *) lock;
+		__entry->ip		= ip;
+	),
+
+	TP_printk("%pF %pF", __entry->lock, (void *) __entry->ip)
+);
+
+DEFINE_EVENT(rt_mutex, rt_mutex_request,
+
+	TP_PROTO(struct rt_mutex *lock, unsigned long ip),
+
+	TP_ARGS(lock, ip)
+);
+
+DEFINE_EVENT(rt_mutex, rt_mutex_acquired,
+
+	TP_PROTO(struct rt_mutex *lock, unsigned long ip),
+
+	TP_ARGS(lock, ip)
+);
+
+DEFINE_EVENT(rt_mutex, rt_mutex_block,
+
+	TP_PROTO(struct rt_mutex *lock, unsigned long ip),
+
+	TP_ARGS(lock, ip)
+);
+
+DEFINE_EVENT(rt_mutex, rt_mutex_abandon,
+
+	TP_PROTO(struct rt_mutex *lock, unsigned long ip),
+
+	TP_ARGS(lock, ip)
+);
+
+DEFINE_EVENT(rt_mutex, rt_mutex_release,
+
+	TP_PROTO(struct rt_mutex *lock, unsigned long ip),
+
+	TP_ARGS(lock, ip)
+);
+
+#define TP_RW_READ		0
+#define TP_RW_WRITE		1
+
+DECLARE_EVENT_CLASS(rwlock,
+
+	TP_PROTO(rwlock_t *lock, int mode, unsigned long ip),
+
+	TP_ARGS(lock, mode, ip),
+
+	TP_STRUCT__entry(
+		__field(	void *,	lock		)
+		__field( 	   int,	mode		)
+		__field( unsigned long,	ip		)
+	),
+
+	TP_fast_assign(
+		__entry->lock		= (void *) lock;
+		__entry->mode		= mode;
+		__entry->ip		= ip;
+	),
+
+	TP_printk("%s %pF %pF", __entry->mode ? "write" : "read", __entry->lock, (void *) __entry->ip)
+);
+
+DEFINE_EVENT(rwlock, rwlock_request,
+
+	TP_PROTO(rwlock_t *lock, int mode, unsigned long ip),
+
+	TP_ARGS(lock, mode, ip)
+);
+
+DEFINE_EVENT(rwlock, rwlock_acquired,
+
+	TP_PROTO(rwlock_t *lock, int mode, unsigned long ip),
+
+	TP_ARGS(lock, mode, ip)
+);
+
+DEFINE_EVENT(rwlock, rwlock_block,
+
+	TP_PROTO(rwlock_t *lock, int mode, unsigned long ip),
+
+	TP_ARGS(lock, mode, ip)
+);
+
+DEFINE_EVENT(rwlock, rwlock_abandon,
+
+	TP_PROTO(rwlock_t *lock, int mode, unsigned long ip),
+
+	TP_ARGS(lock, mode, ip)
+);
+
+DEFINE_EVENT(rwlock, rwlock_release,
+
+	TP_PROTO(rwlock_t *lock, int mode, unsigned long ip),
+
+	TP_ARGS(lock, mode, ip)
+);
+
+DECLARE_EVENT_CLASS(rw_sem,
+
+	TP_PROTO(struct rw_semaphore *lock, int mode, unsigned long ip),
+
+	TP_ARGS(lock, mode, ip),
+
+	TP_STRUCT__entry(
+		__field(	void *,	lock		)
+		__field( 	   int,	mode		)
+		__field( unsigned long,	ip		)
+	),
+
+	TP_fast_assign(
+		__entry->lock		= (void *) lock;
+		__entry->mode		= mode;
+		__entry->ip		= ip;
+	),
+
+	TP_printk("%s %pF %pF", __entry->mode ? "write" : "read", __entry->lock, (void *) __entry->ip)
+);
+
+DEFINE_EVENT(rw_sem, rw_sem_request,
+
+	TP_PROTO(struct rw_semaphore *lock, int mode, unsigned long ip),
+
+	TP_ARGS(lock, mode, ip)
+);
+
+DEFINE_EVENT(rw_sem, rw_sem_acquired,
+
+	TP_PROTO(struct rw_semaphore *lock, int mode, unsigned long ip),
+
+	TP_ARGS(lock, mode, ip)
+);
+
+DEFINE_EVENT(rw_sem, rw_sem_block,
+
+	TP_PROTO(struct rw_semaphore *lock, int mode, unsigned long ip),
+
+	TP_ARGS(lock, mode, ip)
+);
+
+DEFINE_EVENT(rw_sem, rw_sem_abandon,
+
+	TP_PROTO(struct rw_semaphore *lock, int mode, unsigned long ip),
+
+	TP_ARGS(lock, mode, ip)
+);
+
+DEFINE_EVENT(rw_sem, rw_sem_release,
+
+	TP_PROTO(struct rw_semaphore *lock, int mode, unsigned long ip),
+
+	TP_ARGS(lock, mode, ip)
+);
+
 #endif /* _TRACE_LOCK_H */
 
 /* This part must be outside protection */
diff --git a/include/trace/events/sched.h b/include/trace/events/sched.h
index da10aa21bebc..71ded8b8a221 100644
--- a/include/trace/events/sched.h
+++ b/include/trace/events/sched.h
@@ -570,6 +570,102 @@ TRACE_EVENT(sched_wake_idle_without_ipi,
 
 	TP_printk("cpu=%d", __entry->cpu)
 );
+
+
+DECLARE_EVENT_CLASS(sched_preempt_template,
+	TP_PROTO(unsigned long ip, unsigned long pip),
+	TP_ARGS(ip, pip),
+	TP_STRUCT__entry(
+		__field(	unsigned long,       ip      )
+		__field(	unsigned long,       pip      )
+	),
+
+	TP_fast_assign(
+		__entry->ip = ip;
+		__entry->pip = pip;
+	),
+
+	TP_printk("at %pf <- %pf", (void *) __entry->ip, (void *) __entry->pip)
+);
+
+/*
+ * Tracepoint for preempt enable
+ */
+DEFINE_EVENT(sched_preempt_template, sched_preempt_enable,
+	TP_PROTO(unsigned long ip, unsigned long pip),
+	TP_ARGS(ip, pip));
+
+/*
+ * Tracepoint for preempt disable
+ */
+DEFINE_EVENT(sched_preempt_template, sched_preempt_disable,
+	TP_PROTO(unsigned long ip, unsigned long pip),
+	TP_ARGS(ip, pip));
+
+DECLARE_EVENT_CLASS(scheduling,
+	TP_PROTO(unsigned long ip),
+	TP_ARGS(ip),
+	TP_STRUCT__entry(
+		__field(        unsigned long,       ip      )
+	),
+
+	TP_fast_assign(
+		__entry->ip = ip;
+	),
+
+	TP_printk("at %pf", (void *) __entry->ip)
+);
+
+/*
+ * Tracepoint for scheduling start
+ */
+DEFINE_EVENT(scheduling, sched_entry,
+	TP_PROTO(unsigned long ip),
+	TP_ARGS(ip));
+
+/*
+ * Tracepoint for scheduling return
+ */
+DEFINE_EVENT(scheduling, sched_exit,
+	TP_PROTO(unsigned long ip),
+	TP_ARGS(ip));
+
+TRACE_EVENT(set_need_resched,
+	TP_PROTO(struct task_struct *tsk),
+	TP_ARGS(tsk),
+
+	TP_STRUCT__entry(
+		__array( char,  comm,   TASK_COMM_LEN   )
+		__field( pid_t, pid                     )
+	),
+
+	TP_fast_assign(
+		memcpy(__entry->comm, tsk->comm, TASK_COMM_LEN);
+		__entry->pid = tsk->pid;
+	),
+
+	TP_printk("comm=%s pid=%d", __entry->comm, __entry->pid)
+);
+
+TRACE_EVENT_CONDITION(sched_set_state,
+
+	TP_PROTO(int curr_state, int state),
+
+	TP_ARGS(curr_state, state),
+
+	TP_CONDITION(!!curr_state != !!state),
+
+	TP_STRUCT__entry(
+		__field(	int,	state	)
+	),
+
+	TP_fast_assign(
+		__entry->state	= state;
+	),
+
+	TP_printk("%s", __entry->state ? "sleepable" : "runnable")
+);
+
 #endif /* _TRACE_SCHED_H */
 
 /* This part must be outside protection */
diff --git a/kernel/locking/rtmutex.c b/kernel/locking/rtmutex.c
index 838c8a6f662c..0c5738dc152a 100644
--- a/kernel/locking/rtmutex.c
+++ b/kernel/locking/rtmutex.c
@@ -28,6 +28,8 @@
 
 #include "rtmutex_common.h"
 
+#define CREATE_TRACE_POINTS
+#include <trace/events/lock.h>
 /*
  * lock->owner state tracking:
  *
@@ -973,15 +975,22 @@ static inline void rt_spin_lock_fastlock(struct rt_mutex *lock,
 {
 	might_sleep_no_state_check();
 
-	if (likely(rt_mutex_cmpxchg_acquire(lock, NULL, current)))
+	trace_rt_mutex_request(lock, _RET_IP_);
+	if (likely(rt_mutex_cmpxchg_acquire(lock, NULL, current))) {
+		trace_rt_mutex_acquired(lock, _RET_IP_);
 		return;
-	else
+	}
+	else {
+		trace_rt_mutex_block(lock, _RET_IP_);
 		slowfn(lock);
+		trace_rt_mutex_acquired(lock, _RET_IP_);
+	}
 }
 
 static inline void rt_spin_lock_fastunlock(struct rt_mutex *lock,
 					   void  (*slowfn)(struct rt_mutex *lock))
 {
+	trace_rt_mutex_release(lock, _RET_IP_);
 	if (likely(rt_mutex_cmpxchg_release(lock, current, NULL)))
 		return;
 	else
@@ -1931,8 +1940,15 @@ rt_mutex_fastlock(struct rt_mutex *lock, int state,
 				enum rtmutex_chainwalk chwalk,
 				struct ww_acquire_ctx *ww_ctx))
 {
-	if (likely(rt_mutex_cmpxchg_acquire(lock, NULL, current)))
+	int retval;
+
+	trace_rt_mutex_request(lock, _RET_IP_);
+	if (likely(rt_mutex_cmpxchg_acquire(lock, NULL, current))) {
+		trace_rt_mutex_acquired(lock, _RET_IP_);
 		return 0;
+	}
+
+	trace_rt_mutex_block(lock, _RET_IP_);
 
 	/*
 	 * If rt_mutex blocks, the function sched_submit_work will not call
@@ -1943,7 +1959,13 @@ rt_mutex_fastlock(struct rt_mutex *lock, int state,
 	if (unlikely(blk_needs_flush_plug(current)))
 		blk_schedule_flush_plug(current);
 
-	return slowfn(lock, state, NULL, RT_MUTEX_MIN_CHAINWALK, ww_ctx);
+	retval = slowfn(lock, state, NULL, RT_MUTEX_MIN_CHAINWALK, ww_ctx);
+	if (retval)
+		trace_rt_mutex_abandon(lock, _RET_IP_);
+	else
+		trace_rt_mutex_acquired(lock, _RET_IP_);
+
+	return retval;
 }
 
 static inline int
@@ -1956,24 +1978,50 @@ rt_mutex_timed_fastlock(struct rt_mutex *lock, int state,
 				      enum rtmutex_chainwalk chwalk,
 				      struct ww_acquire_ctx *ww_ctx))
 {
+	int retval;
+	trace_rt_mutex_request(lock, _RET_IP_);
 	if (chwalk == RT_MUTEX_MIN_CHAINWALK &&
-	    likely(rt_mutex_cmpxchg_acquire(lock, NULL, current)))
+	    likely(rt_mutex_cmpxchg_acquire(lock, NULL, current))) {
+		trace_rt_mutex_acquired(lock, _RET_IP_);
 		return 0;
+	}
+
+	trace_rt_mutex_block(lock, _RET_IP_);
 
 	if (unlikely(blk_needs_flush_plug(current)))
 		blk_schedule_flush_plug(current);
 
-	return slowfn(lock, state, timeout, chwalk, ww_ctx);
+	retval = slowfn(lock, state, timeout, chwalk, ww_ctx);
+
+	if (retval)
+		trace_rt_mutex_abandon(lock, _RET_IP_);
+	else
+		trace_rt_mutex_acquired(lock, _RET_IP_);
+
+	return retval;
 }
 
 static inline int
 rt_mutex_fasttrylock(struct rt_mutex *lock,
 		     int (*slowfn)(struct rt_mutex *lock))
 {
-	if (likely(rt_mutex_cmpxchg_acquire(lock, NULL, current)))
+	int retval;
+
+	trace_rt_mutex_request(lock, _RET_IP_);
+	if (likely(rt_mutex_cmpxchg_acquire(lock, NULL, current))) {
+		trace_rt_mutex_acquired(lock, _RET_IP_);
 		return 1;
+	}
+
+	trace_rt_mutex_block(lock, _RET_IP_);
+
+	retval = slowfn(lock);
+	if (retval)
+		trace_rt_mutex_abandon(lock, _RET_IP_);
+	else
+		trace_rt_mutex_acquired(lock, _RET_IP_);
 
-	return slowfn(lock);
+	return retval;
 }
 
 /*
@@ -1998,6 +2046,7 @@ rt_mutex_fastunlock(struct rt_mutex *lock,
 	DEFINE_WAKE_Q(wake_q);
 	DEFINE_WAKE_Q(wake_sleeper_q);
 
+	trace_rt_mutex_release(lock, _RET_IP_);
 	if (likely(rt_mutex_cmpxchg_release(lock, current, NULL)))
 		return;
 
@@ -2021,10 +2070,14 @@ static int __sched rt_mutex_lock_state(struct rt_mutex *lock, int state)
 {
 	int ret;
 
+
 	mutex_acquire(&lock->dep_map, 0, 0, _RET_IP_);
 	ret = __rt_mutex_lock_state(lock, state);
-	if (ret)
+	if (ret) {
+
 		mutex_release(&lock->dep_map, 1, _RET_IP_);
+	}
+
 	return ret;
 }
 
diff --git a/kernel/locking/rwlock-rt.c b/kernel/locking/rwlock-rt.c
index aebb7ce25bc6..797d88dbc413 100644
--- a/kernel/locking/rwlock-rt.c
+++ b/kernel/locking/rwlock-rt.c
@@ -6,6 +6,8 @@
 #include "rtmutex_common.h"
 #include <linux/rwlock_types_rt.h>
 
+#include <trace/events/lock.h>
+
 /*
  * RT-specific reader/writer locks
  *
@@ -62,7 +64,7 @@ void __rwlock_biased_rt_init(struct rt_rw_lock *lock, const char *name,
 	lock->rtmutex.save_state = 1;
 }
 
-int __read_rt_trylock(struct rt_rw_lock *lock)
+int inline __read_rt_trylock_notrace(struct rt_rw_lock *lock)
 {
 	int r, old;
 
@@ -79,14 +81,47 @@ int __read_rt_trylock(struct rt_rw_lock *lock)
 	return 0;
 }
 
+
+int __read_rt_trylock(struct rt_rw_lock *lock)
+{
+	int r, old;
+
+	trace_rwlock_request(lock, TP_RW_READ, _RET_IP_);
+	/*
+	 * Increment reader count, if lock->readers < 0, i.e. READER_BIAS is
+	 * set.
+	 */
+	for (r = atomic_read(&lock->readers); r < 0;) {
+		old = atomic_cmpxchg(&lock->readers, r, r + 1);
+		if (likely(old == r)) {
+			trace_rwlock_acquired(lock, TP_RW_READ, _RET_IP_);
+			return 1;
+		}
+		r = old;
+	}
+	trace_rwlock_abandon(lock, TP_RW_READ, _RET_IP_);
+	return 0;
+}
+
 void __sched __read_rt_lock(struct rt_rw_lock *lock)
 {
 	struct rt_mutex *m = &lock->rtmutex;
 	struct rt_mutex_waiter waiter;
 	unsigned long flags;
 
-	if (__read_rt_trylock(lock))
+	trace_rwlock_request(lock, TP_RW_READ, _RET_IP_);
+	if (__read_rt_trylock_notrace(lock)) {
+		trace_rwlock_acquired(lock, TP_RW_READ, _RET_IP_);
 		return;
+	}
+	/*
+	 * Fake the tracepoints... this is an internal use of the rt mutex
+	 * mechanism.
+	 */
+	trace_rt_mutex_request(m, _RET_IP_);
+	trace_rt_mutex_block(m, _RET_IP_);
+
+	trace_rwlock_block(lock, TP_RW_READ, _RET_IP_);
 
 	raw_spin_lock_irqsave(&m->wait_lock, flags);
 	/*
@@ -96,6 +131,8 @@ void __sched __read_rt_lock(struct rt_rw_lock *lock)
 	if (atomic_read(&lock->readers) != WRITER_BIAS) {
 		atomic_inc(&lock->readers);
 		raw_spin_unlock_irqrestore(&m->wait_lock, flags);
+		trace_rt_mutex_acquired(m, _RET_IP_);
+		trace_rwlock_acquired(lock, TP_RW_READ, _RET_IP_);
 		return;
 	}
 
@@ -128,8 +165,12 @@ void __sched __read_rt_lock(struct rt_rw_lock *lock)
 	 * That would put Reader1 behind the writer waiting on
 	 * Reader2 to call read_unlock() which might be unbound.
 	 */
+
+
 	rt_mutex_init_waiter(&waiter, false);
+
 	rt_spin_lock_slowlock_locked(m, &waiter, flags);
+
 	/*
 	 * The slowlock() above is guaranteed to return with the rtmutex is
 	 * now held, so there can't be a writer active. Increment the reader
@@ -137,9 +178,10 @@ void __sched __read_rt_lock(struct rt_rw_lock *lock)
 	 */
 	atomic_inc(&lock->readers);
 	raw_spin_unlock_irqrestore(&m->wait_lock, flags);
+	trace_rt_mutex_acquired(m, _RET_IP_);
 	rt_spin_lock_slowunlock(m);
-
 	debug_rt_mutex_free_waiter(&waiter);
+	trace_rwlock_acquired(lock, TP_RW_READ, _RET_IP_);
 }
 
 void __read_rt_unlock(struct rt_rw_lock *lock)
@@ -147,6 +189,7 @@ void __read_rt_unlock(struct rt_rw_lock *lock)
 	struct rt_mutex *m = &lock->rtmutex;
 	struct task_struct *tsk;
 
+	trace_rwlock_release(lock, TP_RW_READ, _RET_IP_);
 	/*
 	 * sem->readers can only hit 0 when a writer is waiting for the
 	 * active readers to leave the critical region.
@@ -166,6 +209,7 @@ void __read_rt_unlock(struct rt_rw_lock *lock)
 		wake_up_process(tsk);
 
 	raw_spin_unlock_irq(&m->wait_lock);
+
 }
 
 static void __write_unlock_common(struct rt_rw_lock *lock, int bias,
@@ -184,6 +228,7 @@ void __sched __write_rt_lock(struct rt_rw_lock *lock)
 	struct task_struct *self = current;
 	unsigned long flags;
 
+	trace_rwlock_request(lock, TP_RW_WRITE, _RET_IP_);
 	/* Take the rtmutex as a first step */
 	__rt_spin_lock(m);
 
@@ -198,6 +243,7 @@ void __sched __write_rt_lock(struct rt_rw_lock *lock)
 	raw_spin_unlock(&self->pi_lock);
 
 	for (;;) {
+		int blocked = 0;
 		/* Have all readers left the critical region? */
 		if (!atomic_read(&lock->readers)) {
 			atomic_set(&lock->readers, WRITER_BIAS);
@@ -206,10 +252,15 @@ void __sched __write_rt_lock(struct rt_rw_lock *lock)
 			self->saved_state = TASK_RUNNING;
 			raw_spin_unlock(&self->pi_lock);
 			raw_spin_unlock_irqrestore(&m->wait_lock, flags);
+			trace_rwlock_acquired(lock, TP_RW_WRITE, _RET_IP_);
 			return;
 		}
 
 		raw_spin_unlock_irqrestore(&m->wait_lock, flags);
+		if(!blocked) {
+			trace_rwlock_block(lock, TP_RW_WRITE, _RET_IP_);
+			blocked = 1;
+		}
 
 		if (atomic_read(&lock->readers) != 0)
 			schedule();
@@ -227,8 +278,11 @@ int __write_rt_trylock(struct rt_rw_lock *lock)
 	struct rt_mutex *m = &lock->rtmutex;
 	unsigned long flags;
 
-	if (!__rt_mutex_trylock(m))
+	trace_rwlock_request(lock, TP_RW_WRITE, _RET_IP_);
+	if (!__rt_mutex_trylock(m)) {
+		trace_rwlock_abandon(lock, TP_RW_WRITE, _RET_IP_);
 		return 0;
+	}
 
 	atomic_sub(READER_BIAS, &lock->readers);
 
@@ -236,9 +290,11 @@ int __write_rt_trylock(struct rt_rw_lock *lock)
 	if (!atomic_read(&lock->readers)) {
 		atomic_set(&lock->readers, WRITER_BIAS);
 		raw_spin_unlock_irqrestore(&m->wait_lock, flags);
+		trace_rwlock_acquired(lock, TP_RW_WRITE, _RET_IP_);
 		return 1;
 	}
 	__write_unlock_common(lock, 0, flags);
+	trace_rwlock_abandon(lock, TP_RW_WRITE, _RET_IP_);
 	return 0;
 }
 
@@ -247,8 +303,10 @@ void __write_rt_unlock(struct rt_rw_lock *lock)
 	struct rt_mutex *m = &lock->rtmutex;
 	unsigned long flags;
 
+	trace_rwlock_release(lock, TP_RW_WRITE, _RET_IP_);
 	raw_spin_lock_irqsave(&m->wait_lock, flags);
 	__write_unlock_common(lock, WRITER_BIAS, flags);
+
 }
 
 /* Map the reader biased implementation */
diff --git a/kernel/locking/rwsem-rt.c b/kernel/locking/rwsem-rt.c
index 26991ddb6c5a..2cd2bd659ecb 100644
--- a/kernel/locking/rwsem-rt.c
+++ b/kernel/locking/rwsem-rt.c
@@ -5,6 +5,8 @@
 #include <linux/sched/signal.h>
 #include <linux/export.h>
 
+#include <trace/events/lock.h>
+
 #include "rtmutex_common.h"
 
 /*
@@ -62,7 +64,7 @@ void __rwsem_init(struct rw_semaphore *sem, const char *name,
 }
 EXPORT_SYMBOL(__rwsem_init);
 
-int __down_read_trylock(struct rw_semaphore *sem)
+int __down_read_trylock_notrace(struct rw_semaphore *sem)
 {
 	int r, old;
 
@@ -79,15 +81,52 @@ int __down_read_trylock(struct rw_semaphore *sem)
 	return 0;
 }
 
+int __down_read_trylock(struct rw_semaphore *sem)
+{
+	int r, old;
+
+	trace_rw_sem_request(sem, TP_RW_READ, _RET_IP_);
+	/*
+	 * Increment reader count, if sem->readers < 0, i.e. READER_BIAS is
+	 * set.
+	 */
+	for (r = atomic_read(&sem->readers); r < 0;) {
+		old = atomic_cmpxchg(&sem->readers, r, r + 1);
+		if (likely(old == r)) {
+			trace_rw_sem_acquired(sem, TP_RW_READ, _RET_IP_);
+			return 1;
+		}
+		r = old;
+	}
+	trace_rw_sem_abandon(sem, TP_RW_READ, _RET_IP_);
+	return 0;
+}
+
 void __sched __down_read(struct rw_semaphore *sem)
 {
 	struct rt_mutex *m = &sem->rtmutex;
 	struct rt_mutex_waiter waiter;
 
-	if (__down_read_trylock(sem))
+	trace_rw_sem_request(sem, TP_RW_READ, _RET_IP_);
+
+	if (__down_read_trylock_notrace(sem)) {
+		trace_rw_sem_acquired(sem, TP_RW_READ, _RET_IP_);
 		return;
+	}
+
+	trace_rw_sem_block(sem, TP_RW_READ, _RET_IP_);
 
 	might_sleep();
+	/*
+	 * Fake the tracepoints... this is an internal use of the rt mutex
+	 * mechanism... and so it is following the rules. Here I am forcing
+	 * the tracepoints to take place earlier...
+	 *
+	 * This is odd, I know...
+	 */
+	trace_rt_mutex_request(m, _RET_IP_);
+	trace_rt_mutex_block(m, _RET_IP_);
+
 	raw_spin_lock_irq(&m->wait_lock);
 	/*
 	 * Allow readers as long as the writer has not completely
@@ -96,6 +135,8 @@ void __sched __down_read(struct rw_semaphore *sem)
 	if (atomic_read(&sem->readers) != WRITER_BIAS) {
 		atomic_inc(&sem->readers);
 		raw_spin_unlock_irq(&m->wait_lock);
+		trace_rt_mutex_acquired(m, _RET_IP_);
+		trace_rw_sem_acquired(sem, TP_RW_READ, _RET_IP_);
 		return;
 	}
 
@@ -129,9 +170,12 @@ void __sched __down_read(struct rw_semaphore *sem)
 	 * Reader2 to call up_read() which might be unbound.
 	 */
 	rt_mutex_init_waiter(&waiter, false);
+
 	rt_mutex_slowlock_locked(m, TASK_UNINTERRUPTIBLE, NULL,
 				 RT_MUTEX_MIN_CHAINWALK, NULL,
 				 &waiter);
+
+
 	/*
 	 * The slowlock() above is guaranteed to return with the rtmutex is
 	 * now held, so there can't be a writer active. Increment the reader
@@ -139,9 +183,13 @@ void __sched __down_read(struct rw_semaphore *sem)
 	 */
 	atomic_inc(&sem->readers);
 	raw_spin_unlock_irq(&m->wait_lock);
+
+	trace_rt_mutex_acquired(m, _RET_IP_);
+
 	__rt_mutex_unlock(m);
 
 	debug_rt_mutex_free_waiter(&waiter);
+	trace_rw_sem_acquired(sem, TP_RW_READ, _RET_IP_);
 }
 
 void __up_read(struct rw_semaphore *sem)
@@ -149,6 +197,7 @@ void __up_read(struct rw_semaphore *sem)
 	struct rt_mutex *m = &sem->rtmutex;
 	struct task_struct *tsk;
 
+	trace_rw_sem_release(sem, TP_RW_READ, _RET_IP_);
 	/*
 	 * sem->readers can only hit 0 when a writer is waiting for the
 	 * active readers to leave the critical region.
@@ -186,9 +235,12 @@ static int __sched __down_write_common(struct rw_semaphore *sem, int state)
 	struct rt_mutex *m = &sem->rtmutex;
 	unsigned long flags;
 
+	trace_rw_sem_request(sem, TP_RW_WRITE, _RET_IP_);
 	/* Take the rtmutex as a first step */
-	if (__rt_mutex_lock_state(m, state))
+	if (__rt_mutex_lock_state(m, state)) {
+		trace_rw_sem_abandon(sem, TP_RW_WRITE, _RET_IP_);
 		return -EINTR;
+	}
 
 	/* Force readers into slow path */
 	atomic_sub(READER_BIAS, &sem->readers);
@@ -196,22 +248,32 @@ static int __sched __down_write_common(struct rw_semaphore *sem, int state)
 
 	set_current_state(state);
 	for (;;) {
+		int blocked = 0;
 		raw_spin_lock_irqsave(&m->wait_lock, flags);
 		/* Have all readers left the critical region? */
 		if (!atomic_read(&sem->readers)) {
 			atomic_set(&sem->readers, WRITER_BIAS);
 			__set_current_state(TASK_RUNNING);
 			raw_spin_unlock_irqrestore(&m->wait_lock, flags);
+			trace_rw_sem_acquired(sem, TP_RW_WRITE, _RET_IP_);
 			return 0;
 		}
 
 		if (signal_pending_state(state, current)) {
+			trace_rw_sem_abandon(sem, TP_RW_WRITE, _RET_IP_);
 			__set_current_state(TASK_RUNNING);
 			__up_write_unlock(sem, 0, flags);
 			return -EINTR;
 		}
+
 		raw_spin_unlock_irqrestore(&m->wait_lock, flags);
 
+		if (!blocked) {
+			trace_rw_sem_block(sem, TP_RW_WRITE, _RET_IP_);
+			blocked = 1;
+		}
+
+
 		if (atomic_read(&sem->readers) != 0) {
 			schedule();
 			set_current_state(state);
@@ -234,8 +296,12 @@ int __down_write_trylock(struct rw_semaphore *sem)
 	struct rt_mutex *m = &sem->rtmutex;
 	unsigned long flags;
 
-	if (!__rt_mutex_trylock(m))
+	trace_rw_sem_request(sem, TP_RW_WRITE, _RET_IP_);
+
+	if (!__rt_mutex_trylock(m)) {
+		trace_rw_sem_abandon(sem, TP_RW_WRITE, _RET_IP_);
 		return 0;
+	}
 
 	atomic_sub(READER_BIAS, &sem->readers);
 
@@ -243,9 +309,11 @@ int __down_write_trylock(struct rw_semaphore *sem)
 	if (!atomic_read(&sem->readers)) {
 		atomic_set(&sem->readers, WRITER_BIAS);
 		raw_spin_unlock_irqrestore(&m->wait_lock, flags);
+		trace_rw_sem_acquired(sem, TP_RW_WRITE, _RET_IP_);
 		return 1;
 	}
 	__up_write_unlock(sem, 0, flags);
+	trace_rw_sem_abandon(sem, TP_RW_WRITE, _RET_IP_);
 	return 0;
 }
 
@@ -254,6 +322,7 @@ void __up_write(struct rw_semaphore *sem)
 	struct rt_mutex *m = &sem->rtmutex;
 	unsigned long flags;
 
+	trace_rw_sem_release(sem, TP_RW_WRITE, _RET_IP_);
 	raw_spin_lock_irqsave(&m->wait_lock, flags);
 	__up_write_unlock(sem, WRITER_BIAS, flags);
 }
@@ -266,4 +335,7 @@ void __downgrade_write(struct rw_semaphore *sem)
 	raw_spin_lock_irqsave(&m->wait_lock, flags);
 	/* Release it and account current as reader */
 	__up_write_unlock(sem, WRITER_BIAS - 1, flags);
+	trace_rw_sem_release(sem, TP_RW_WRITE, _RET_IP_);
+	trace_rw_sem_request(sem, TP_RW_READ, _RET_IP_);
+	trace_rw_sem_acquired(sem, TP_RW_READ, _RET_IP_);
 }
diff --git a/kernel/sched/core.c b/kernel/sched/core.c
index 1ab422da7706..46007cfb3a9f 100644
--- a/kernel/sched/core.c
+++ b/kernel/sched/core.c
@@ -90,6 +90,12 @@ int sysctl_sched_rt_runtime = 950000;
 /* CPUs with isolated domains */
 cpumask_var_t cpu_isolated_map;
 
+void trace_set_current_state(int state_value)
+{
+	trace_sched_set_state(current->state, state_value);
+}
+EXPORT_SYMBOL(trace_set_current_state);
+
 /*
  * __task_rq_lock - lock the rq @p resides on.
  */
@@ -506,6 +512,8 @@ void resched_curr(struct rq *rq)
 
 	cpu = cpu_of(rq);
 
+	trace_set_need_resched(curr);
+
 	if (cpu == smp_processor_id()) {
 		set_tsk_need_resched(curr);
 		set_preempt_need_resched();
@@ -570,6 +578,15 @@ void resched_cpu(int cpu)
 	raw_spin_unlock_irqrestore(&rq->lock, flags);
 }
 
+/*
+ * XXX: Moved to here because of the tracepoint
+ */
+inline void set_tsk_need_resched(struct task_struct *tsk)
+{
+
+	set_tsk_thread_flag(tsk,TIF_NEED_RESCHED);
+}
+
 #ifdef CONFIG_SMP
 #ifdef CONFIG_NO_HZ_COMMON
 /*
@@ -2080,6 +2097,7 @@ try_to_wake_up(struct task_struct *p, unsigned int state, int wake_flags)
 	 */
 	raw_spin_lock_irqsave(&p->pi_lock, flags);
 	smp_mb__after_spinlock();
+
 	if (!(p->state & state)) {
 		/*
 		 * The task might be running due to a spinlock sleeper
@@ -2545,6 +2563,7 @@ void wake_up_new_task(struct task_struct *p)
 
 	activate_task(rq, p, ENQUEUE_NOCLOCK);
 	p->on_rq = TASK_ON_RQ_QUEUED;
+	trace_sched_waking(p);
 	trace_sched_wakeup_new(p);
 	check_preempt_curr(rq, p, WF_FORK);
 #ifdef CONFIG_SMP
@@ -2818,6 +2837,12 @@ asmlinkage __visible void schedule_tail(struct task_struct *prev)
 
 	rq = finish_task_switch(prev);
 	balance_callback(rq);
+	/*
+	 * This is a special case: the newly created task has just
+	 * switch the context for the fist time. It is returning from
+	 * schedule by the first time, in this path.
+	 */
+	trace_sched_exit(CALLER_ADDR0);
 	preempt_enable();
 
 	if (current->set_child_tid)
@@ -3150,6 +3175,7 @@ static inline void preempt_latency_start(int val)
 		current->preempt_disable_ip = ip;
 #endif
 		trace_preempt_off(CALLER_ADDR0, ip);
+		trace_sched_preempt_disable(CALLER_ADDR0, ip);
 	}
 }
 
@@ -3181,8 +3207,10 @@ NOKPROBE_SYMBOL(preempt_count_add);
  */
 static inline void preempt_latency_stop(int val)
 {
-	if (preempt_count() == val)
+	if (preempt_count() == val) {
 		trace_preempt_on(CALLER_ADDR0, get_lock_parent_ip());
+		trace_sched_preempt_enable(CALLER_ADDR0, get_lock_parent_ip());
+	}
 }
 
 void preempt_count_sub(int val)
@@ -3364,6 +3392,8 @@ static void __sched notrace __schedule(bool preempt)
 	struct rq *rq;
 	int cpu;
 
+	trace_sched_entry(CALLER_ADDR0);
+
 	cpu = smp_processor_id();
 	rq = cpu_rq(cpu);
 	prev = rq->curr;
@@ -3434,11 +3464,14 @@ static void __sched notrace __schedule(bool preempt)
 		/* Also unlocks the rq: */
 		rq = context_switch(rq, prev, next, &rf);
 	} else {
+		trace_sched_switch(preempt, prev, next);
 		rq->clock_update_flags &= ~(RQCF_ACT_SKIP|RQCF_REQ_SKIP);
 		rq_unlock_irq(rq, &rf);
 	}
 
 	balance_callback(rq);
+
+	trace_sched_exit(CALLER_ADDR0);
 }
 
 void __noreturn do_task_dead(void)
@@ -3472,7 +3505,7 @@ void __noreturn do_task_dead(void)
 		cpu_relax();
 }
 
-static inline void sched_submit_work(struct task_struct *tsk)
+void sched_submit_work(struct task_struct *tsk)
 {
 	if (!tsk->state)
 		return;
diff --git a/kernel/softirq.c b/kernel/softirq.c
index d0688a7680af..c93c7feab047 100644
--- a/kernel/softirq.c
+++ b/kernel/softirq.c
@@ -302,6 +302,7 @@ void __local_bh_disable_ip(unsigned long ip, unsigned int cnt)
 		current->preempt_disable_ip = get_lock_parent_ip();
 #endif
 		trace_preempt_off(CALLER_ADDR0, get_lock_parent_ip());
+		trace_sched_preempt_disable(CALLER_ADDR0, ip);
 	}
 }
 EXPORT_SYMBOL(__local_bh_disable_ip);
@@ -471,6 +472,7 @@ asmlinkage __visible void do_softirq(void)
 	local_irq_restore(flags);
 }
 
+
 /*
  * This function must run with irqs disabled!
  */
@@ -1252,3 +1254,26 @@ unsigned int __weak arch_dynirq_lower_bound(unsigned int from)
 {
 	return from;
 }
+
+/*
+ * This is the minimal irq_exit needed to be able to use tracepoints
+ * in the late IRQ context. Do not use it, unless you are deadling with
+ * tracepoints in the very late irq context.
+ */
+void __irq_exit_late_tp(void)
+{
+	rcu_irq_exit();
+	trace_hardirq_exit(); /* must be last! */
+}
+
+/*
+ * This is the minimal irq_enter needed to be able to use tracepoints
+ * from early IRQ context. Do not use it, unless you are deadling with
+ * tracepoints in the very early irq context.
+ */
+void __irq_enter_early_tp(void)
+{
+	rcu_irq_enter();
+	trace_hardirq_enter();
+}
+
diff --git a/kernel/trace/trace_functions_graph.c b/kernel/trace/trace_functions_graph.c
index 23c0b0cb5fb9..8e4134d287d8 100644
--- a/kernel/trace/trace_functions_graph.c
+++ b/kernel/trace/trace_functions_graph.c
@@ -408,7 +408,7 @@ int trace_graph_entry(struct ftrace_graph_ent *trace)
 	if (tracing_thresh)
 		return 1;
 
-	local_irq_save(flags);
+	raw_local_irq_save(flags);
 	cpu = raw_smp_processor_id();
 	data = per_cpu_ptr(tr->trace_buffer.data, cpu);
 	disabled = atomic_inc_return(&data->disabled);
@@ -420,7 +420,7 @@ int trace_graph_entry(struct ftrace_graph_ent *trace)
 	}
 
 	atomic_dec(&data->disabled);
-	local_irq_restore(flags);
+	raw_local_irq_restore(flags);
 
 	return ret;
 }
@@ -482,7 +482,7 @@ void trace_graph_return(struct ftrace_graph_ret *trace)
 	int cpu;
 	int pc;
 
-	local_irq_save(flags);
+	raw_local_irq_save(flags);
 	cpu = raw_smp_processor_id();
 	data = per_cpu_ptr(tr->trace_buffer.data, cpu);
 	disabled = atomic_inc_return(&data->disabled);
@@ -491,7 +491,7 @@ void trace_graph_return(struct ftrace_graph_ret *trace)
 		__trace_graph_return(tr, trace, flags, pc);
 	}
 	atomic_dec(&data->disabled);
-	local_irq_restore(flags);
+	raw_local_irq_restore(flags);
 }
 
 void set_graph_array(struct trace_array *tr)
diff --git a/kernel/trace/trace_irqsoff.c b/kernel/trace/trace_irqsoff.c
index 7758bc0617cb..2eb5c158a59a 100644
--- a/kernel/trace/trace_irqsoff.c
+++ b/kernel/trace/trace_irqsoff.c
@@ -14,6 +14,7 @@
 #include <linux/module.h>
 #include <linux/ftrace.h>
 
+#include <trace/events/irq.h>
 #include "trace.h"
 
 static struct trace_array		*irqsoff_trace __read_mostly;
@@ -483,6 +484,7 @@ inline void print_irqtrace_events(struct task_struct *curr)
  */
 void trace_hardirqs_on(void)
 {
+	trace_local_irq_enable(CALLER_ADDR0);
 	if (!preempt_trace() && irq_trace())
 		stop_critical_timing(CALLER_ADDR0, CALLER_ADDR1);
 }
@@ -490,6 +492,7 @@ EXPORT_SYMBOL(trace_hardirqs_on);
 
 void trace_hardirqs_off(void)
 {
+	trace_local_irq_disable(CALLER_ADDR0);
 	if (!preempt_trace() && irq_trace())
 		start_critical_timing(CALLER_ADDR0, CALLER_ADDR1);
 }
@@ -497,18 +500,40 @@ EXPORT_SYMBOL(trace_hardirqs_off);
 
 __visible void trace_hardirqs_on_caller(unsigned long caller_addr)
 {
+	trace_local_irq_enable(caller_addr);
 	if (!preempt_trace() && irq_trace())
 		stop_critical_timing(CALLER_ADDR0, caller_addr);
 }
 EXPORT_SYMBOL(trace_hardirqs_on_caller);
 
+static DEFINE_PER_CPU(int, debug_recursion);
+
 __visible void trace_hardirqs_off_caller(unsigned long caller_addr)
 {
 	if (!preempt_trace() && irq_trace())
 		start_critical_timing(CALLER_ADDR0, caller_addr);
+	if (this_cpu_inc_return(debug_recursion) == 1)
+		trace_local_irq_disable(caller_addr);
+	else
+		dump_stack();
+	this_cpu_dec(debug_recursion);
 }
 EXPORT_SYMBOL(trace_hardirqs_off_caller);
 
+__visible void trace_hardirqs_on_caller_notrace(unsigned long caller_addr)
+{
+	if (!preempt_trace() && irq_trace())
+		stop_critical_timing(CALLER_ADDR0, caller_addr);
+}
+EXPORT_SYMBOL(trace_hardirqs_on_caller_notrace);
+
+__visible void trace_hardirqs_off_caller_notrace(unsigned long caller_addr)
+{
+	if (!preempt_trace() && irq_trace())
+		start_critical_timing(CALLER_ADDR0, caller_addr);
+}
+EXPORT_SYMBOL(trace_hardirqs_off_caller_notrace);
+
 #endif /* CONFIG_PROVE_LOCKING */
 #endif /*  CONFIG_IRQSOFF_TRACER */
 
diff --git a/tools/lib/str_error_r.c b/tools/lib/str_error_r.c
index d6d65537b0d9..6aad8308a0ac 100644
--- a/tools/lib/str_error_r.c
+++ b/tools/lib/str_error_r.c
@@ -22,6 +22,6 @@ char *str_error_r(int errnum, char *buf, size_t buflen)
 {
 	int err = strerror_r(errnum, buf, buflen);
 	if (err)
-		snprintf(buf, buflen, "INTERNAL ERROR: strerror_r(%d, %p, %zd)=%d", errnum, buf, buflen, err);
+		snprintf(buf, buflen, "INTERNAL ERROR: strerror_r(%d, [buf], %zd)=%d", errnum, buflen, err);
 	return buf;
 }
diff --git a/tools/lib/subcmd/pager.c b/tools/lib/subcmd/pager.c
index 5ba754d17952..9997a8805a82 100644
--- a/tools/lib/subcmd/pager.c
+++ b/tools/lib/subcmd/pager.c
@@ -30,10 +30,13 @@ static void pager_preexec(void)
 	 * have real input
 	 */
 	fd_set in;
+	fd_set exception;
 
 	FD_ZERO(&in);
+	FD_ZERO(&exception);
 	FD_SET(0, &in);
-	select(1, &in, NULL, &in, NULL);
+	FD_SET(0, &exception);
+	select(1, &in, NULL, &exception, NULL);
 
 	setenv("LESS", "FRSX", 0);
 }
diff --git a/tools/perf/Build b/tools/perf/Build
index b48ca40fccf9..d5cd38376795 100644
--- a/tools/perf/Build
+++ b/tools/perf/Build
@@ -14,6 +14,7 @@ perf-y += builtin-record.o
 perf-y += builtin-report.o
 perf-y += builtin-stat.o
 perf-y += builtin-timechart.o
+perf-y += builtin-task_model.o
 perf-y += builtin-top.o
 perf-y += builtin-script.o
 perf-y += builtin-kmem.o
@@ -39,6 +40,7 @@ paths += -DPERF_MAN_PATH="BUILD_STR($(mandir_SQ))"
 
 CFLAGS_builtin-help.o      += $(paths)
 CFLAGS_builtin-timechart.o += $(paths)
+CFLAGS_builtin-task_model.o += $(paths)
 CFLAGS_perf.o              += -DPERF_HTML_PATH="BUILD_STR($(htmldir_SQ))"	\
 			      -DPERF_EXEC_PATH="BUILD_STR($(perfexecdir_SQ))"	\
 			      -DPREFIX="BUILD_STR($(prefix_SQ))"
diff --git a/tools/perf/Makefile.config b/tools/perf/Makefile.config
index 63f534a0902f..3dc58709faae 100644
--- a/tools/perf/Makefile.config
+++ b/tools/perf/Makefile.config
@@ -200,6 +200,7 @@ CFLAGS += -ggdb3
 CFLAGS += -funwind-tables
 CFLAGS += -Wall
 CFLAGS += -Wextra
+CFLAGS += -lgvc -lcgraph
 CFLAGS += -std=gnu99
 
 CXXFLAGS += -std=gnu++11 -fno-exceptions -fno-rtti
diff --git a/tools/perf/builtin-help.c b/tools/perf/builtin-help.c
index bd1fedef3d1c..325608e48765 100644
--- a/tools/perf/builtin-help.c
+++ b/tools/perf/builtin-help.c
@@ -442,6 +442,7 @@ int cmd_help(int argc, const char **argv)
 #ifdef HAVE_LIBAUDIT_SUPPORT
 		"trace",
 #endif
+		"task_model",
 	NULL };
 	const char *builtin_help_usage[] = {
 		"perf help [--all] [--man|--web|--info] [command]",
diff --git a/tools/perf/builtin-record.c b/tools/perf/builtin-record.c
index 0c95ffefb6cc..8f6f7f15936c 100644
--- a/tools/perf/builtin-record.c
+++ b/tools/perf/builtin-record.c
@@ -1148,6 +1148,7 @@ static int __cmd_record(struct record *rec, int argc, const char **argv)
 			perf_evlist__disable(rec->evlist);
 			disabled = true;
 		}
+		 sleep(5);
 	}
 	trigger_off(&auxtrace_snapshot_trigger);
 	trigger_off(&switch_output_trigger);
diff --git a/tools/perf/builtin-task_model.c b/tools/perf/builtin-task_model.c
new file mode 100644
index 000000000000..88aec9f7f459
--- /dev/null
+++ b/tools/perf/builtin-task_model.c
@@ -0,0 +1,1627 @@
+/*
+ * (C) Copyright 2017-2018: Daniel Bristot de Oliveira <daniel@bristot.me>
+ *
+ * Author:
+ *     Daniel Bristot de Oliveira <daniel@bristot.me>
+ *     Thie skeleton was taken from builtin-timechart.c
+ *     which the author is Arjan van de Ven <arjan@linux.intel.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; version 2
+ * of the License.
+ */
+
+#include "builtin.h"
+#include <subcmd/parse-options.h>
+#include "util/evlist.h"
+
+/* Automaton */
+#include <stdio.h>
+#include <stdlib.h>
+#include <graphviz/gvc.h>
+
+const char *irqs_entry_point[] = {
+	"apic_timer_interrupt",
+	"call_function_single_interrupt",
+	"reschedule_interrupt",
+	"common_interrupt",
+	NULL };
+
+const char *schedule_entry_point[] = {
+	"___preempt_schedule",
+	"___preempt_schedule_notrace",
+        "preempt_schedule_common",
+	"preempt_schedule_irq",
+	"preempt_schedule_notrace",
+	"schedule",
+	"schedule_idle",
+        NULL };
+
+struct config {
+	const char *irqs_automaton;
+	const char *nmi_automaton;
+};
+
+struct automaton;
+
+struct nmi {
+};
+
+/*
+ * 256 IRQs per CPU, but the first 32 are not interrupts
+ * but traps & faults. OK, the 2 is the NMI, but they are
+ * considered apart.
+ */
+#define IRQ_FIRST_IRQ	32
+#define IRQ_LAST_IRQ	256
+#define IRQS_PER_CPU	(IRQ_LAST_IRQ - IRQ_FIRST_IRQ)
+
+struct irq {
+};
+
+struct thread {
+	int blocked;
+};
+
+struct cpu {
+	int id;
+	int need_resched;
+	int start_processing;
+	int irqs_disabled_by_hardware;
+	struct nmi nmi;
+	struct irq irq[IRQS_PER_CPU];
+	unsigned int curr_pid;
+	int in_irq;
+	int in_nmi;
+	int in_sched;
+	unsigned long need_resched_time;
+
+	/*
+	 * work around missing early and late
+	 * irq events
+	 */
+	int workaround_preempt_disabled;
+	int workaround_irq_disabled;
+};
+
+struct system {
+	int		nrcpus;
+	struct thread 	thread;
+	struct cpu 	*cpus;
+};
+
+#define MODE_NMI	1
+#define MODE_IRQ	2
+#define MODE_THREAD	3
+
+struct task_model {
+	struct perf_tool	tool;
+	struct system		system;
+	struct machine		*host;
+	unsigned int		pid;
+	const char 		*nmi;
+	const char 		*irq;
+	const char 		*thread;
+	struct automaton	*automaton;
+	int mode;
+
+};
+
+#define debug printf
+
+static struct automaton *automaton_open(const char *path);
+static int automaton_event(struct automaton *aut, const char *event);
+
+static int open_automaton(struct task_model *tmodel)
+{
+	const char *path;
+
+	if (tmodel->nmi)
+		 path = tmodel->nmi;
+	else if (tmodel->irq)
+		path = tmodel->irq;
+	else
+		path = tmodel->thread;
+
+	debug("Reference model: %s\n", path);
+
+	tmodel->automaton = automaton_open(path);
+
+	if (!tmodel->automaton)
+		return 0;
+		
+	if (tmodel->mode == MODE_THREAD){
+		/*
+		 * The initial state of the thread is in the
+		 * idle thread. Idle thread never enables preemption
+		 * so... we need to "disable" the preeption in the
+		 * automaton too.
+		 */
+		automaton_event(tmodel->automaton, "preempt_disable");
+	}
+	return 1;
+}
+
+static int init_nmi(struct nmi *nmi __maybe_unused)
+{
+	return 0;
+}
+
+static int init_irqs(struct irq *irq_vector __maybe_unused)
+{
+	return 0;
+}
+
+static int init_thread(struct thread *thread)
+{
+	thread->blocked = 0;
+	return 0;
+}
+
+static void reset_cpu(struct cpu *c)
+{
+	c->start_processing = 0;
+	c->irqs_disabled_by_hardware = 0;
+	c->need_resched = 0;
+	c->in_irq = c->in_nmi = 0;
+	c->in_sched = 0;
+	c->curr_pid = 0;
+	c->need_resched_time = 0;
+	c->workaround_irq_disabled = c->workaround_preempt_disabled = 0;
+}
+
+
+static int initiate_cpu(struct cpu *c, int id)
+{
+	c->id = id;
+	init_nmi(&c->nmi);
+	init_irqs(c->irq);
+	reset_cpu(c);
+	return 1;
+}
+
+static void reset_thread(struct thread *thread)
+{
+	init_thread(thread);
+}
+
+static void reset_system(struct system *s)
+{
+	int i;
+	reset_thread(&s->thread);
+	for (i = 0; i < s->nrcpus; i++)
+		reset_cpu(&s->cpus[i]);
+}
+
+
+static void automaton_reset(struct automaton *aut);
+static void reset_model(struct task_model *tmodel)
+{
+	printf("===== reseting model =====\n");
+	reset_system(&tmodel->system);
+	automaton_reset(tmodel->automaton);
+	if (tmodel->mode == MODE_THREAD){
+		/*
+		 * The initial state of the thread is in the
+		 * idle thread. Idle thread never enables preemption
+		 * so... we need to "disable" the preeption in the
+		 * automaton too.
+		 */
+		automaton_event(tmodel->automaton, "preempt_disable");
+	}
+}
+
+/*
+ * Initialize structures
+ */
+static int process_header(struct perf_file_section *section __maybe_unused,
+			  struct perf_header *ph,
+			  int feat,
+			  int fd __maybe_unused,
+			  void *data)
+{
+	struct task_model *tmodel = data;
+	int i;
+	int nrcpus;
+
+	switch (feat) {
+	case HEADER_NRCPUS:
+		nrcpus = ph->env.nr_cpus_avail;
+
+		tmodel->system.nrcpus = nrcpus;
+		init_thread(&tmodel->system.thread);
+
+		/*
+		 * Initiate data structures, for each CPU.
+		 */
+		tmodel->system.cpus = malloc(sizeof(struct cpu) * nrcpus);
+		for (i = 0; i < nrcpus; i++) {
+			initiate_cpu(&tmodel->system.cpus[i], i);
+		}
+
+		break;
+	default:
+		break;
+	}
+
+	return 0;
+}
+
+/*
+ * Helping functions.
+ */
+inline struct cpu *cpu_of_system(struct system *s, int cpu)
+{
+	if (cpu >= s->nrcpus)
+		return NULL;
+
+	return &s->cpus[cpu];
+}
+
+inline struct nmi *nmi_of_a_cpu(struct cpu *c)
+{
+	return &c->nmi;
+}
+
+inline struct irq *irq_of_a_cpu(struct cpu *c, int vector)
+{
+	vector -= IRQ_FIRST_IRQ;
+
+	if (vector < 0 || vector > IRQS_PER_CPU)
+		return NULL;
+
+	return &c->irq[vector];
+}
+
+/*
+ * Automaton part.
+ */
+static GVC_t *context;
+
+struct automaton {
+	char *path;
+	FILE *fp;
+	Agraph_t *automaton;
+	Agnode_t *root;
+	Agnode_t *curr;
+};
+
+static void automaton_create_context(void)
+{
+	context = gvContext();
+}
+
+static void automaton_destroy_context(void)
+{
+	gvFreeContext(context);
+}
+
+static Agnode_t *automaton_goto_first_node(Agraph_t *automaton)
+{
+	Agnode_t *root = agfstnode(automaton);
+	Agedge_t *edge = agfstout(automaton, root);
+	root = aghead(edge);
+	return root;
+}
+
+static void automaton_reset(struct automaton *aut)
+{
+	aut->curr =  aut->root;
+}
+
+static struct automaton *automaton_open(const char *path)
+{
+	struct automaton *aut;
+
+	aut = malloc(sizeof(struct automaton));
+	if (!aut)
+		goto out_err;
+
+	aut->path = malloc(strlen(path));
+	if (!path)
+		goto out_free_aut;
+
+	memcpy(aut->path, path, strlen(path));
+
+	aut->fp = fopen(path, "r");
+	if (!aut->fp)
+		goto out_free_path;
+
+	aut->automaton = agread(aut->fp, 0);
+	if (!aut->automaton)
+		goto out_close_file;
+
+	aut->root = automaton_goto_first_node(aut->automaton);
+	automaton_reset(aut);
+
+	return aut;
+ /* XXX: update the close function as well */
+out_close_file:
+	fclose(aut->fp);
+out_free_path:
+	free(aut->path);
+out_free_aut:
+	free(aut);
+out_err:
+	return NULL;
+}
+
+static Agnode_t *automaton_event_destination(struct automaton *aut,
+					     const char *event)
+{
+	Agedge_t *e;
+	char shh_label[] = "label";
+	char *label;
+
+	for (e = agfstout(aut->automaton, aut->curr);
+			e;
+			e = agnxtout(aut->automaton, e)) {
+		label = agget(e, shh_label);
+		if (!strchr(label, '\\')) {
+			if (strcmp(label, event) == 0)
+				return aghead(e);
+		} else {
+			char label_v[240];
+			char *label_aux = label_v;
+			char *next;
+			memcpy(label_v, label, strlen(label)+1);
+			do {
+				next = strchr(label_aux, '\\');
+				if (next) {
+					*next = '\0';
+					next+=2;
+				}
+				if (strcmp(label_aux, event) == 0)
+					return aghead(e);
+
+				label_aux = next;
+			} while (label_aux);
+
+
+		}
+	}
+	return NULL;
+	
+}
+
+static int automaton_event(struct automaton *aut, const char *event)
+{
+	Agnode_t *n;
+
+	n = automaton_event_destination(aut, event);
+	if (!n) 
+		return 0;
+
+	aut->curr = n;
+	return 1;
+}
+
+static const char *automaton_current_state(struct automaton *aut)
+{
+	char shh_label[] = "label";
+	return (const char *) agget(aut->curr, shh_label);
+}
+
+
+static int automaton_is_a_final_node(Agnode_t *n)
+{
+	char shh_shape[] = "shape";
+	char *shape = agget(n, shh_shape);
+	return !strcmp(shape, "doublecircle");
+}
+
+#if 0
+static void automaton_close(struct automaton *aut)
+{
+	agclose(aut->automaton);
+	fclose(aut->fp);
+	free(aut->path);
+	free(aut);
+}
+
+#endif
+
+/*
+ * Printing information
+ */
+enum context {
+	CONTEXT_NMI,
+	CONTEXT_IRQ,
+	CONTEXT_THREAD,
+};
+
+static int is_safe_state(struct task_model *tmodel, struct perf_sample *sample)
+{
+	Agnode_t *n;
+	if (automaton_is_a_final_node(tmodel->automaton->curr))
+		return 1;
+
+	if (tmodel->mode == MODE_THREAD) {
+		if (sample->tid == 0) {
+			n = automaton_event_destination(tmodel->automaton,
+							"preempt_enable");
+			if(n && automaton_is_a_final_node(n))
+				return 1;
+		}
+	}
+
+	return 0;
+}
+
+
+static void print_header()
+{
+printf("+---->  +=thread of interest - .=other threads\n");
+printf("| +-> T=Thread - I=IRQ - N=NMI\n");
+printf("| |\n");
+printf("| |   TID  |    timestamp     | cpu |           event              | state   | safe?  \n");
+/*      . T       0    91899.457565    [000]            local_irq_disable -> q64      */
+}
+static void event_not_expected(struct automaton *aut, const char *event,
+			       struct perf_sample *sample)
+{
+	printf("\n\t%lu.%0.6lu event %s is not expected in state %s\n",
+		sample->time / 1000000000 , (sample->time % 1000000000) / 1000,
+		event,
+		automaton_current_state(aut));
+}
+
+static void event_print_before(struct task_model *tmodel, struct perf_sample *sample,
+			       const char *event)
+{
+	char prefix[12]; /* \t65535:I\0 */
+	if (tmodel->mode == MODE_THREAD) {
+		struct cpu *c = cpu_of_system(&tmodel->system, sample->cpu);
+
+		sprintf(prefix, "%s %s %6u",
+				sample->tid == tmodel->pid ? "+" : ".",
+				c->in_nmi ? "N " : (c->in_irq ? "I " : "T "),
+				c->curr_pid);
+
+	} else {
+		prefix[0] = '\0';
+	}
+
+	printf("%9s    %lu.%0.6lu    [%0.3d]    %25s", prefix,
+			sample->time / 1000000000,
+			(sample->time % 1000000000) / 1000,
+			sample->cpu,
+			event);
+
+} 
+
+static void event_print_after(struct task_model *tmodel, struct perf_sample *sample,
+			      const char *event)
+{
+	struct cpu *c = cpu_of_system(&tmodel->system, sample->cpu);
+	if (c->start_processing)
+		printf(" -> %8s %s\n", automaton_current_state(tmodel->automaton),
+		       is_safe_state(tmodel, sample) ? "safe" : "");
+
+}
+
+inline void start_processing(struct cpu *c)
+{
+	printf("CPU %d: starting processing\n", c->id);
+
+	c->start_processing = 1;
+}
+
+inline int ready_to_process(struct cpu *c)
+{
+	return c->start_processing;
+}
+
+static int process_lost_event(struct perf_tool *tool __maybe_unused,
+				union perf_event *event,
+				struct perf_sample *sample __maybe_unused,
+				struct machine *machine __maybe_unused)
+{
+	struct task_model *model = container_of(tool, struct task_model, tool);
+
+	reset_model(model);
+
+	printf("lost events: %lu", event->lost.lost);
+	return 0;
+}
+
+static int is_hw_entry(struct machine *machine, struct perf_sample *sample,
+		       unsigned long long ip)
+{
+	int i;
+	struct addr_location al;
+	char *function, *module;
+	const char *hw = irqs_entry_point[0];
+
+	if (!sample) {
+		perror("sample not set!\n");
+		exit(1);
+	}
+
+	if (machine__resolve(machine, &al, sample) < 0) {
+		fprintf(stderr, "problem processing event\n");
+		function = NULL;
+	} else {
+		function = machine__resolve_kernel_addr(machine, &ip, &module);
+	}
+
+	/*
+	 * If it was disabled by hardware, take a note and wait
+	 * for the next event to inject it.
+	 */
+	for (i = 0; hw && function; hw = irqs_entry_point[++i]) {
+		if (strcmp(function, hw) == 0)
+			return 1;
+	}
+	return 0;
+}
+
+static int is_sched_entry(struct machine *machine, struct perf_sample *sample,
+		       unsigned long long ip)
+{
+	int i;
+	struct addr_location al;
+	char *function, *module;
+	const char *sched = schedule_entry_point[0];
+
+	if (!sample) {
+		perror("sample not set!\n");
+		exit(1);
+	}
+
+	if (machine__resolve(machine, &al, sample) < 0) {
+		fprintf(stderr, "problem processing event\n");
+		function = NULL;
+	} else {
+		function = machine__resolve_kernel_addr(machine, &ip, &module);
+	}
+
+	for (i = 0; sched && function; sched = schedule_entry_point[++i]) {
+		if (strcmp(function, sched) == 0)
+			return 1;
+	}
+	return 0;
+}
+
+static int check_entry_exit_error(struct machine *machine, struct perf_sample *sample,
+		       unsigned long long ip)
+{
+	struct addr_location al;
+	char *function, *module;
+
+	if (!sample) {
+		perror("sample not set!\n");
+		exit(1);
+	}
+
+	if (machine__resolve(machine, &al, sample) < 0) {
+		fprintf(stderr, "problem processing event\n");
+		function = NULL;
+	} else {
+		function = machine__resolve_kernel_addr(machine, &ip, &module);
+	}
+
+	/*
+	 * Error entry and exit disables the preemption inconditionally.
+	 * But there are some paths, like in the scheduler, in which it
+	 * is called with interrupts already disabled.
+	 * This is not a problem as they don't enable interrupts, so...
+	 * Just ignore this as a known issue.
+	 */
+	if (!strcmp(function, "error_entry") || !strcmp(function, "error_exit"))
+		return 1;
+	return 0;
+}
+
+static int process_event(struct task_model *tmodel, struct perf_sample *sample,
+		  const char *event)
+{
+	int retval;
+
+	event_print_before(tmodel, sample, event);
+	retval = automaton_event(tmodel->automaton, event);
+	if (!retval) {
+		event_not_expected(tmodel->automaton, event, sample);
+		reset_model(tmodel);
+	}
+	event_print_after(tmodel, sample, event);
+	return retval;
+
+}
+
+static int process_irq_disable(struct task_model *tmodel,
+			       struct perf_evsel *evsel,
+			       struct perf_sample *sample,
+			       struct machine *machine)
+{
+	int retval;
+	const char *event = "local_irq_disable";
+	struct cpu *c = cpu_of_system(&tmodel->system, sample->cpu);
+	unsigned long long ip = perf_evsel__intval(evsel, sample, "ip");
+
+	/* Nothing to do for the NMI automaton */
+	switch(tmodel->mode) {
+	case MODE_IRQ:
+		/*
+		 * If it was disabled by hardware, take a note and wait
+		 * for the next event to inject it.
+		 */
+		if (is_hw_entry(machine, sample, ip)) {
+			c->in_irq = 1;
+			c->irqs_disabled_by_hardware = 1;
+			goto out;
+		}
+
+		process_event(tmodel, sample, event);
+		break;
+	case MODE_THREAD:
+		if (is_hw_entry(machine, sample, ip)) {
+			c->in_irq = 1;
+			c->irqs_disabled_by_hardware = 1;
+			event = "hw_local_irq_disable";
+		}
+
+		/*
+		 * the process_event is not used here because of the
+		 * exception
+		 */
+		event_print_before(tmodel, sample, event);
+		retval = automaton_event(tmodel->automaton, event);
+		if (!retval) {
+			if (!check_entry_exit_error(machine, sample, ip)) {
+				event_not_expected(tmodel->automaton, event, sample);
+				reset_model(tmodel);
+			}
+		}
+		event_print_after(tmodel, sample, event);
+		c->workaround_irq_disabled = 1;
+		break;
+	case MODE_NMI:
+	default:
+		return 0;
+	}
+
+out:
+	return 0;
+}
+
+static int process_irq_enable(struct task_model *tmodel,
+			      struct perf_evsel *evsel __maybe_unused,
+			      struct perf_sample *sample,
+			      struct machine *machine __maybe_unused)
+{
+	struct cpu *c = cpu_of_system(&tmodel->system, sample->cpu);
+	const char *event = "local_irq_enable";
+
+	/*
+	 * Event valid only in the IRQ and THREAD models.
+	 */
+	switch (tmodel->mode) {
+	case MODE_IRQ:
+		process_event(tmodel, sample, event);
+		break;
+	case MODE_THREAD:
+		if (c->irqs_disabled_by_hardware)
+			event = "hw_local_irq_enable";
+
+		process_event(tmodel, sample, event);
+
+		break;
+	default:
+		break;
+	}
+	c->in_irq = 0;
+	c->irqs_disabled_by_hardware = 0;
+	c->workaround_irq_disabled = 0;
+
+	return 0;
+}
+
+static int process_nmi_entry(struct task_model *tmodel,
+			     struct perf_evsel *evsel __maybe_unused,
+			     struct perf_sample *sample)
+{
+	const char *event = "nmi_entry";
+	struct cpu *c = cpu_of_system(&tmodel->system, sample->cpu);
+
+	c->in_nmi = 1;
+	process_event(tmodel, sample, event);
+
+	return 0;
+}
+
+static int process_nmi_exit(struct task_model *tmodel,
+			    struct perf_evsel *evsel __maybe_unused,
+			    struct perf_sample *sample)
+{
+	const char *event = "nmi_exit";
+	struct cpu *c = cpu_of_system(&tmodel->system, sample->cpu);
+
+	process_event(tmodel, sample, event);
+	c->in_nmi = 0;
+
+	return 0;
+}
+
+static int process_int_entry(struct task_model *tmodel,
+			     struct perf_evsel *evsel,
+			     struct perf_sample *sample)
+{
+	const char *event;
+	struct cpu *c = cpu_of_system(&tmodel->system, sample->cpu);
+
+	/*
+	 * This event is only recoginized in the IRQ model.
+	 */
+	if (tmodel->mode != MODE_IRQ)
+		return 0;
+
+	/*
+	 * It is not possible to capture the IRQ event, currently. So,
+	 * we take not of the last irq disable. If it was caused by the
+	 * entry point of interrupts, c->irqs_disabled_by_hardware is 1
+	 *
+	 * If irqs were disabled by hardware, then we need to inject the
+	 * 'irq' event, then the irqs disabled event that was postponed.
+	 * Finally, the 'irq_entry' event can be processed.
+	 */
+	if (!c->irqs_disabled_by_hardware) {
+		event = "irq_entry";
+		process_event(tmodel, sample, event);
+	} else {
+		unsigned int vector = (int) perf_evsel__intval(evsel, sample, "vector");
+		/*
+		 * If the vector is the one of the IRQ being traced, we
+		 * inject all the events.
+		 *
+		 * If not, consider it another IRQ, not requiring all
+		 * events.
+		 */
+		if (tmodel->pid == vector) {
+			/*
+			 * The expected behavior is 'irq' ->
+			 *	   'local_irq_disable_hw' ->
+			 *		      'irq_entry';
+			 */
+			event = "irq";
+			if (!process_event(tmodel, sample, event))
+				return 0;
+
+			event = "local_irq_disable_hw";
+			if (!process_event(tmodel, sample, event))
+				return 0;
+
+			event = "irq_entry";
+			process_event(tmodel, sample, event);
+		} else {
+			/*
+			 * If this is not the IRQ of interest, the
+			 * expected event is 'local_irq_disable_hw_n'
+			 */
+			event = "local_irq_disable_hw_n";
+			process_event(tmodel, sample, event);
+		}
+	}
+
+	return 0;
+}
+
+static int process_int_exit(struct task_model *tmodel,
+			    struct perf_evsel *evsel,
+			    struct perf_sample *sample)
+{
+	const char *event = "irq_exit";
+	unsigned int vector;
+
+	/*
+	 * This event is only recoginized in the IRQ model.
+	 */
+	if (tmodel->mode != MODE_IRQ)
+		return 0;
+
+	vector = (unsigned int) perf_evsel__intval(evsel, sample, "vector");
+
+	if (tmodel->pid == vector)
+		process_event(tmodel, sample, event);
+
+	return 0;
+}
+
+static int process_thread_waking(struct task_model *tmodel,
+				 struct perf_evsel *evsel,
+				 struct perf_sample *sample)
+{
+	unsigned int pid;
+	const char *event = "sched_waking";
+
+	/*
+	 * This event is only part of the thread model.
+	 */
+	if (tmodel->mode != MODE_THREAD)
+		return 0;
+
+	pid = (int) perf_evsel__intval(evsel, sample, "pid");
+	/*
+	 * This event is processed only if the thread of interest is
+	 * the one being awakened.
+	 */
+	if (tmodel->pid == pid)
+		process_event(tmodel, sample, event);
+
+	return 0;
+}
+
+static int process_thread_set_state(struct task_model *tmodel,
+				    struct perf_evsel *evsel,
+				    struct perf_sample *sample)
+{
+	int state;
+	unsigned int pid;
+	const char *event = "sched_set_state_sleepable";
+
+	/*
+	 * This event is only part of the thread model.
+	 */
+	if (tmodel->mode != MODE_THREAD)
+		return 0;
+
+	pid = sample->tid;
+
+	/*
+	 * This event is processed only if the thread of interest is
+	 * changing its own state.
+	 */
+	if (tmodel->pid == pid) {
+		state = (int) perf_evsel__intval(evsel, sample, "state");
+		/*
+		 * state == 0: Thread is runnable.
+		 * state != 0: Thread is sleepable.
+		 */
+		if (!state)
+			event = "sched_set_state_runnable";
+
+		process_event(tmodel, sample, event);
+	}
+
+	return 0;
+}
+
+static int process_thread_need_resched(struct task_model *tmodel,
+				       struct perf_evsel *evsel __maybe_unused,
+				       struct perf_sample *sample)
+{
+	const char *event = "sched_need_resched";
+	struct cpu *c = cpu_of_system(&tmodel->system, sample->cpu);
+	/*
+	 * This event is only part of the thread model.
+	 */
+	if (tmodel->mode != MODE_THREAD)
+		return 0;
+
+	if(!process_event(tmodel, sample, event))
+		return 0;
+
+	c->need_resched = 1;
+	c->need_resched_time = sample->time;
+
+	return 0;
+}
+
+static int process_thread_switch(struct task_model *tmodel,
+				 struct perf_evsel *evsel,
+				 struct perf_sample *sample)
+{
+	unsigned int pid_in, pid_out;
+	const char *event_out = "sched_switch_out_o";
+	const char *event_in = "sched_switch_in_o";
+	struct thread *thread = &tmodel->system.thread;
+	struct cpu *c = cpu_of_system(&tmodel->system, sample->cpu);
+
+	/*
+	 * This event is only present in the Thread model.
+	 */
+	if (tmodel->mode != MODE_THREAD)
+		return 0;
+
+	pid_out = (unsigned int) perf_evsel__intval(evsel, sample, "prev_pid");
+	pid_in = (unsigned int) perf_evsel__intval(evsel, sample, "next_pid");
+
+	/*
+	 * If need resched is set, the event will always be a
+	 * preemption.
+	 *
+	 * If the task is blocked, it will not suspend, but block.
+	 *
+	 * If neither block or preemption, then the thread is leaving
+	 * the processor by itself.
+	 */
+	if (pid_out == tmodel->pid) {
+		if (c->need_resched)
+			event_out = "sched_switch_preempt";
+		else if (thread->blocked)
+			event_out = "sched_switch_blocking";
+		else
+			event_out = "sched_switch_suspend";
+	}
+
+	if (pid_in == tmodel->pid)
+		event_in = "sched_switch_in";
+
+	/*
+	 * First, the current thread goes out.
+	 */
+	if(!process_event(tmodel, sample, event_out))
+		return 0;
+
+	/*
+	 * Then, the next gets in.
+	 */
+	if(!process_event(tmodel, sample, event_in))
+		return 1;
+
+	if (pid_in == tmodel->pid)
+		thread->blocked = 0;
+
+	/*
+	 * Need resched is cleared after the switch.
+	 */
+	if (c->need_resched)
+		printf("need_resched delay = %lu\n", sample->time - c->need_resched_time);
+
+	c->need_resched = 0;
+	c->curr_pid = pid_in;
+
+	c->need_resched_time = 0;
+	return 0;
+}
+
+static int process_thread_sched_entry(struct task_model *tmodel,
+				      struct perf_evsel *evsel __maybe_unused,
+				      struct perf_sample *sample)
+{
+	const char *event = "schedule_entry";
+
+	/*
+	 * This event is only present in the Thread model.
+	 */
+	if (tmodel->mode != MODE_THREAD)
+		return 0;
+
+	process_event(tmodel, sample, event);
+
+	return 0;
+}
+
+static int process_thread_sched_exit(struct task_model *tmodel,
+				     struct perf_evsel *evsel __maybe_unused,
+				     struct perf_sample *sample)
+{
+	const char *event = "schedule_exit";
+
+	if (tmodel->mode != MODE_THREAD)
+		return 0;
+
+	process_event(tmodel, sample, event);
+
+	return 0;
+}
+
+static int
+process_thread_preempt_disable(struct task_model *tmodel,
+			       struct perf_evsel *evsel __maybe_unused,
+			       struct perf_sample *sample,
+			       struct machine *machine __maybe_unused)
+{
+	const char *event = "preempt_disable";
+	struct cpu *c = cpu_of_system(&tmodel->system, sample->cpu);
+	unsigned long long ip;
+
+	if (tmodel->mode != MODE_THREAD)
+		return 0;
+
+	ip = perf_evsel__intval(evsel, sample, "ip");
+
+	if (is_sched_entry(machine, sample, ip)) {
+		c->in_sched = 1;
+		event = "preempt_disable_sched";
+	}
+
+	process_event(tmodel, sample, event);
+	c->workaround_preempt_disabled = 1;
+	return 0;
+}
+
+static int
+process_thread_preempt_enable(struct task_model *tmodel,
+			      struct perf_evsel *evsel __maybe_unused,
+			      struct perf_sample *sample)
+{
+	const char *event = "preempt_enable";
+	struct cpu *c = cpu_of_system(&tmodel->system, sample->cpu);
+
+	if (tmodel->mode != MODE_THREAD)
+		return 0;
+
+	if (c->in_sched) {
+		event = "preempt_enable_sched";
+		c->in_sched = 0;
+	}
+
+	process_event(tmodel, sample, event);
+
+	c->workaround_preempt_disabled = 0;
+	return 0;
+}
+
+static int
+process_thread_rt_mutex_abandon(struct task_model *tmodel,
+				struct perf_evsel *evsel __maybe_unused,
+				struct perf_sample *sample)
+{
+	const char *event = "mutex_abandon";
+
+	if (tmodel->mode != MODE_THREAD || sample->tid != tmodel->pid)
+		return 0;
+
+	process_event(tmodel, sample, event);
+
+	return 0;
+}
+
+static int
+process_thread_rt_mutex_block(struct task_model *tmodel,
+			      struct perf_evsel *evsel __maybe_unused,
+			      struct perf_sample *sample)
+{
+	const char *event = "mutex_blocked";
+
+	if (tmodel->mode != MODE_THREAD || sample->tid != tmodel->pid)
+		return 0;
+	tmodel->system.thread.blocked = 1;
+	process_event(tmodel, sample, event);
+
+	return 0;
+}
+
+static int
+process_thread_rt_mutex_acquired(struct task_model *tmodel,
+				 struct perf_evsel *evsel __maybe_unused,
+				 struct perf_sample *sample)
+{
+	const char *event = "mutex_acquired";
+
+	if (tmodel->mode != MODE_THREAD || sample->tid != tmodel->pid)
+		return 0;
+
+	process_event(tmodel, sample, event);
+
+	return 0;
+}
+
+static int
+process_thread_rt_mutex_request(struct task_model *tmodel,
+				struct perf_evsel *evsel __maybe_unused,
+				struct perf_sample *sample)
+{
+	const char *event = "mutex_lock";
+
+	if (tmodel->mode != MODE_THREAD || sample->tid != tmodel->pid)
+		return 0;
+
+	process_event(tmodel, sample, event);
+
+	return 0;
+}
+
+static int
+process_thread_rw_abandon(struct task_model *tmodel,
+			  struct perf_evsel *evsel __maybe_unused,
+			  struct perf_sample *sample)
+{
+	unsigned int rw;
+	const char *event = "read_abandon";
+
+	if (tmodel->mode != MODE_THREAD || sample->tid != tmodel->pid)
+		return 0;
+
+	rw = (unsigned int) perf_evsel__intval(evsel, sample, "mode");
+	if (rw)
+		event = "write_abandon";
+
+	process_event(tmodel, sample, event);
+
+	return 0;
+}
+
+static int
+process_thread_rw_blocked(struct task_model *tmodel,
+			  struct perf_evsel *evsel __maybe_unused,
+			  struct perf_sample *sample)
+{
+	unsigned int rw;
+	const char *event = "read_blocked";
+
+	if (tmodel->mode != MODE_THREAD || sample->tid != tmodel->pid)
+		return 0;
+
+	rw = (unsigned int) perf_evsel__intval(evsel, sample, "mode");
+	if (rw)
+		event = "write_blocked";
+
+	tmodel->system.thread.blocked = 1;
+	process_event(tmodel, sample, event);
+
+	return 0;
+}
+
+static int
+process_thread_rw_acquired(struct task_model *tmodel,
+			   struct perf_evsel *evsel __maybe_unused,
+			   struct perf_sample *sample)
+{
+	unsigned int rw;
+	const char *event = "read_acquired";
+
+	if (tmodel->mode != MODE_THREAD || sample->tid != tmodel->pid)
+		return 0;
+
+	rw = (unsigned int) perf_evsel__intval(evsel, sample, "mode");
+	if (rw)
+		event = "write_acquired";
+
+	process_event(tmodel, sample, event);
+
+	return 0;
+}
+
+static int
+process_thread_rw_request(struct task_model *tmodel,
+			  struct perf_evsel *evsel __maybe_unused,
+			  struct perf_sample *sample)
+{
+	unsigned int rw;
+	const char *event = "read_lock";
+
+	if (tmodel->mode != MODE_THREAD || sample->tid != tmodel->pid)
+		return 0;
+
+	rw = (unsigned int) perf_evsel__intval(evsel, sample, "mode");
+	if (rw)
+		event = "write_lock";
+
+	process_event(tmodel, sample, event);
+
+	return 0;
+}
+
+static int
+process_workaround(struct task_model *tmodel,
+			  struct perf_evsel *evsel __maybe_unused,
+			  struct perf_sample *sample,
+			  struct machine *machine __maybe_unused)
+{
+	unsigned long long ip;
+	struct addr_location al;
+	char *function, *module;
+	const char *event;
+	struct cpu *c = cpu_of_system(&tmodel->system, sample->cpu);
+
+	if (tmodel->mode != MODE_THREAD || sample->tid != tmodel->pid)
+		return 0;
+
+	printf("working around: irq=%d preempt=%d\n", c->workaround_irq_disabled, c->workaround_preempt_disabled);
+	if(c->workaround_preempt_disabled && c->workaround_irq_disabled)
+		return;
+
+	ip = (unsigned long long) perf_evsel__intval(evsel, sample, "ip");
+	if (machine__resolve(machine, &al, sample) < 0) {
+		fprintf(stderr, "prepare yourself to an error\n");
+		return 0;
+	} else {
+		function = machine__resolve_kernel_addr(machine, &ip, &module);
+	}
+
+	if (strcmp(function, "irq_enter")) {
+		if (!c->workaround_irq_disabled) {
+			event = "hw_local_irq_disable";
+			process_event(tmodel, sample, event);
+		}
+		
+		if(!c->workaround_preempt_disabled) {
+			event = "preempt_disable";
+			process_event(tmodel, sample, event);
+		}
+	}
+
+	if (strcmp(function, "irq_exit")) {
+		if (!c->workaround_irq_disabled) {
+			event = "hw_local_irq_enable";
+			process_event(tmodel, sample, event);
+		}
+		
+		if(!c->workaround_preempt_disabled) {
+			event = "preempt_enable";
+			process_event(tmodel, sample, event);
+		}
+	}
+
+	return 0;
+}
+
+/*
+ * perf infra-structure
+ */
+typedef int (*tracepoint_handler)(struct task_model *tmodel,
+				  struct perf_evsel *evsel,
+				  struct perf_sample *sample,
+				  struct machine *machine);
+
+static inline int can_start_processing(struct task_model *tmodel,
+				       struct perf_evsel *evsel,
+				       struct perf_sample *sample)
+{
+	switch(tmodel->mode)
+	{
+		case MODE_NMI:
+			/*
+			 * nmi_entry is the last enabled event for NMI.
+			 * Moreover, in this way we know there is no
+			 * nmi running
+			 */
+			if (evsel->handler == process_nmi_entry)
+				return 1;
+			break;
+		case MODE_IRQ:
+			/*
+			 * This is the last event enabled for IRQ.
+			 * If this takes place, we know interrupts
+			 * are on and no IRQ is taking place.
+			 */
+                        if (evsel->handler == process_irq_disable)
+                                return 1;
+			break;
+		case MODE_THREAD:
+			/*
+			 * in the return of the sched_exit, to the idle, we don't have
+			 * our task running, neither it is preempted. There might be the
+			 * case that it is blocked, but... we have to fix this later.
+			 */
+			if ((sample->tid != 0) && (sample->tid != tmodel->pid) && (evsel->handler == process_thread_sched_exit))
+				return 1;
+			break;
+		default:
+			printf("mode not set?\n");
+			exit(1);
+	}
+
+	return 0;
+	
+}
+
+static int process_sample_event(struct perf_tool *tool,
+				union perf_event *event __maybe_unused,
+				struct perf_sample *sample,
+				struct perf_evsel *evsel,
+				struct machine *machine)
+{
+	tracepoint_handler f = evsel->handler;
+	struct task_model *tmodel = container_of(tool, struct task_model, tool);
+
+	if (evsel->handler != NULL) {
+		if (ready_to_process(&tmodel->system.cpus[sample->cpu])) {
+			return f(tmodel, evsel, sample, machine);
+		} else {
+			if (can_start_processing(tmodel, evsel, sample))
+				start_processing(&tmodel->system.cpus[sample->cpu]);
+		}
+	}
+
+	return 0;
+}
+
+static int __cmd_task_model(struct task_model *tmodel)
+{
+	const struct perf_evsel_str_handler model_tracepoints[] = {
+
+		{ "irq_vectors:nmi_exit",			process_nmi_exit		},
+			/* nmi_entry should be the last for NMI */
+		{ "irq_vectors:nmi_entry",			process_nmi_entry		},
+		{ "irq_vectors:move_cleanup_exit",		process_int_exit		},
+		{ "irq_vectors:move_cleanup_entry",		process_int_entry		},
+		{ "irq_vectors:user_defined_exit",		process_int_exit		},
+		{ "irq_vectors:user_defined_entry",		process_int_entry		},
+		{ "irq_vectors:thermal_apic_exit",		process_int_exit		},
+		{ "irq_vectors:thermal_apic_entry", 		process_int_entry		},
+		{ "irq_vectors:deferred_error_apic_exit",	process_int_exit		},
+		{ "irq_vectors:deferred_error_apic_entry",	process_int_entry		},
+		{ "irq_vectors:threshold_apic_exit",		process_int_exit		},
+		{ "irq_vectors:threshold_apic_entry",		process_int_entry		},
+		{ "irq_vectors:call_function_single_exit",	process_int_exit		},
+		{ "irq_vectors:call_function_single_entry",	process_int_entry		},
+		{ "irq_vectors:call_function_exit",		process_int_exit		},
+		{ "irq_vectors:call_function_entry",		process_int_entry		},
+		{ "irq_vectors:irq_work_exit",			process_int_exit		},
+		{ "irq_vectors:irq_work_entry",			process_int_entry		},
+		{ "irq_vectors:x86_platform_ipi_exit",		process_int_exit		},
+		{ "irq_vectors:x86_platform_ipi_entry",		process_int_entry		},
+		{ "irq_vectors:error_apic_exit",		process_int_exit		},
+		{ "irq_vectors:error_apic_entry",		process_int_entry		},
+		{ "irq_vectors:spurious_apic_exit",		process_int_exit		},
+		{ "irq_vectors:spurious_apic_entry",		process_int_entry		},
+		{ "irq_vectors:reschedule_exit",		process_int_exit		},
+		{ "irq_vectors:reschedule_entry",		process_int_entry		},
+		{ "irq_vectors:local_timer_exit",		process_int_exit		},
+		{ "irq_vectors:local_timer_entry",		process_int_entry		},
+		{ "irq:local_irq_disable",			process_irq_disable		},
+			/* Local IRQ enable should be the last for the IRQ */
+		{ "irq:local_irq_enable",			process_irq_enable		},
+		{ "lock:rt_mutex_abandon",			process_thread_rt_mutex_abandon	},
+		{ "lock:rt_mutex_block",			process_thread_rt_mutex_block	},
+		{ "lock:rt_mutex_acquired",			process_thread_rt_mutex_acquired},
+		{ "lock:rt_mutex_request",			process_thread_rt_mutex_request	},
+		{ "lock:rwlock_abandon",			process_thread_rw_abandon	},
+		{ "lock:rwlock_block",				process_thread_rw_blocked	},
+		{ "lock:rwlock_acquired",			process_thread_rw_acquired	},
+		{ "lock:rwlock_request",			process_thread_rw_request	},
+		{ "lock:rw_sem_abandon",			process_thread_rw_abandon	},
+		{ "lock:rw_sem_block",				process_thread_rw_blocked	},
+		{ "lock:rw_sem_acquired",			process_thread_rw_acquired	},
+		{ "lock:rw_sem_request",			process_thread_rw_request	},
+		{ "sched:sched_waking",				process_thread_waking		},
+		{ "sched:sched_switch",				process_thread_switch		},
+		{ "sched:set_need_resched",			process_thread_need_resched	},
+		{ "sched:sched_set_state",			process_thread_set_state	},
+		{ "sched:sched_preempt_disable",		process_thread_preempt_disable	},
+		{ "sched:sched_preempt_enable",			process_thread_preempt_enable	},
+		{ "sched:sched_entry",				process_thread_sched_entry	},
+			/* sched_exit should be the last for THREAD */
+		{ "sched:sched_exit",				process_thread_sched_exit	},
+	};
+
+	struct perf_data_file file = {
+		.path = input_name,
+		.mode = PERF_DATA_MODE_READ,
+	};
+
+	int ret = -EINVAL;
+
+	struct perf_session *session = perf_session__new(&file, false,
+							 &tmodel->tool);
+
+	if (symbol__init(&session->header.env) < 0)
+		goto out_delete;
+
+	if (session == NULL)
+		return -1;
+
+	if (symbol__init(&session->header.env) < 0)
+		goto out_delete;
+
+	(void)perf_header__process_sections(&session->header,
+					    perf_data_file__fd(session->file),
+					    tmodel,
+					    process_header);
+
+	if (!perf_session__has_traces(session, "task_model record"))
+		goto out_delete;
+
+	if (perf_session__set_tracepoints_handlers(session,
+						   model_tracepoints)) {
+		pr_err("Initializing session tracepoint handlers failed\n");
+		goto out_delete;
+	}
+
+	print_header();
+
+	ret = perf_session__process_events(session);
+	if (ret)
+		goto out_delete;
+
+out_delete:
+	automaton_destroy_context();
+	perf_session__delete(session);
+	return ret;
+}
+
+#define NOPERF	"--exclude-perf"
+
+static int task_model__record(struct task_model *tmodel __maybe_unused, int argc, const char **argv)
+{
+	unsigned int rec_argc, i, j;
+	const char **rec_argv;
+	const char **p;
+	unsigned int record_elems;
+
+	// "record", "-o", "/dev/shm/perf.data", "-a", "-R", "-m", "1G",
+	const char * const common_args[] = {
+		"record", "-a", "-R", "-m", "1G",
+	};
+	unsigned int common_args_nr = ARRAY_SIZE(common_args);
+
+	const char * const int_args[] = {
+		"-e", "irq_vectors:nmi_exit",
+		"-e", "irq_vectors:nmi_entry",
+		"-e", "irq_vectors:move_cleanup_exit",
+		"-e", "irq_vectors:move_cleanup_entry",
+		"-e", "irq_vectors:user_defined_exit",
+		"-e", "irq_vectors:user_defined_entry",
+		"-e", "irq_vectors:thermal_apic_exit",
+		"-e", "irq_vectors:thermal_apic_entry",
+		"-e", "irq_vectors:deferred_error_apic_exit",
+		"-e", "irq_vectors:deferred_error_apic_entry",
+		"-e", "irq_vectors:threshold_apic_exit",
+		"-e", "irq_vectors:threshold_apic_entry",
+#ifdef SMP
+		"-e", "irq_vectors:call_function_single_exit",
+		"-e", "irq_vectors:call_function_single_entry",
+		"-e", "irq_vectors:call_function_exit",
+		"-e", "irq_vectors:call_function_entry",
+		"-e", "irq_vectors:reschedule_exit",
+		"-e", "irq_vectors:reschedule_entry",
+#endif
+
+#if 0 /* Perf uses this IRQ */
+		"-e", "irq_vectors:irq_work_exit",
+#endif
+		"-e", "irq_vectors:irq_work_entry",
+
+		"-e", "irq_vectors:x86_platform_ipi_exit",
+		"-e", "irq_vectors:x86_platform_ipi_entry",
+		"-e", "irq_vectors:error_apic_exit",
+		"-e", "irq_vectors:error_apic_entry",
+		"-e", "irq_vectors:spurious_apic_exit",
+		"-e", "irq_vectors:spurious_apic_entry",
+		"-e", "irq_vectors:local_timer_entry",
+		"-e", "irq_vectors:local_timer_exit",
+		"-e", "irq:local_irq_enable",
+		"-e", "irq:local_irq_disable",
+	};
+	unsigned int int_args_nr = ARRAY_SIZE(int_args);
+
+	const char * const tasks_args[] = {
+		/* TASKS */
+		"-e", "irq:softirq_raise",
+		"-e", "sched:sched_set_state",
+		"-e", "sched:sched_waking",
+		"-e", "sched:sched_switch",
+		"-e", "sched:set_need_resched",
+		"-e", "sched:sched_exit",
+		"-e", "sched:sched_entry",
+		"-e", "sched:sched_preempt_disable",
+		"-e", "sched:sched_preempt_enable",
+		"-e", "lock:rw_sem_release",
+		"-e", "lock:rw_sem_abandon",
+		"-e", "lock:rw_sem_block",
+		"-e", "lock:rw_sem_acquired",
+		"-e", "lock:rw_sem_request",
+		"-e", "lock:rwlock_release",
+		"-e", "lock:rwlock_abandon",
+		"-e", "lock:rwlock_block",
+		"-e", "lock:rwlock_acquired",
+		"-e", "lock:rwlock_request",
+		"-e", "lock:rt_mutex_release",
+		"-e", "lock:rt_mutex_abandon",
+		"-e", "lock:rt_mutex_block",
+		"-e", "lock:rt_mutex_acquired",
+		"-e", "lock:rt_mutex_request",
+	};
+	unsigned int tasks_args_nr = ARRAY_SIZE(tasks_args);
+
+	record_elems = common_args_nr + tasks_args_nr +
+		int_args_nr;
+
+	rec_argc = record_elems + argc;
+	rec_argv = calloc(rec_argc + 1, sizeof(char *));
+
+	if (rec_argv == NULL)
+		return -ENOMEM;
+
+	p = rec_argv;
+	for (i = 0; i < common_args_nr; i++)
+		*p++ = strdup(common_args[i]);
+
+	for (i = 0; i < tasks_args_nr; i++)
+		*p++ = strdup(tasks_args[i]);
+
+	for (i = 0; i < int_args_nr; i++)
+		*p++ = strdup(int_args[i]);
+
+	for (j = 0; j < (unsigned int)argc; j++)
+		*p++ = argv[j];
+
+	return cmd_record(rec_argc, rec_argv);
+}
+
+int cmd_task_model(int argc, const char **argv)
+{
+	struct task_model tmodel = {
+		.tool = {
+			.lost		= process_lost_event,
+			.lost_samples	= process_lost_event,
+			.sample		= process_sample_event,
+			.ordered_events	= true,
+		},
+		.pid	=	0,
+	};
+	const struct option task_model_options[] = {
+	OPT_STRING('i', "input", &input_name, "file", "input file name"),
+	OPT_END()
+	};
+	const char * const task_model_subcommands[] = { "record", "report", NULL };
+	const char *task_model_usage[] = {
+		"perf task_model [<options>] {record}",
+		NULL
+	};
+
+	const struct option task_model_record_options[] = {
+		OPT_END()
+	};
+
+	const struct option task_model_report_options[] = {
+		OPT_UINTEGER('P', "PID", &tmodel.pid,
+				"Thread's pid or IRQ's vector"),
+		OPT_STRING('N', "nmi", &tmodel.nmi, "file", "NMI automata model"),
+		OPT_STRING('I', "irq", &tmodel.irq, "file", "IRQ automata model"),
+		OPT_STRING('T', "thread", &tmodel.thread, "file", "IRQ automata model"),
+		OPT_END()
+	};
+
+	const char * const task_model_record_usage[] = {
+		"perf task_model record [<options>]",
+		NULL
+	};
+
+	const char * const task_model_report_usage[] = {
+		"perf task_model report [<options>]",
+		NULL
+	};
+
+	argc = parse_options_subcommand(argc, argv, task_model_options, task_model_subcommands,
+			task_model_usage, PARSE_OPT_STOP_AT_NON_OPTION);
+
+	if (argc && !strncmp(argv[0], "rec", 3)) {
+		argc = parse_options(argc, argv, task_model_record_options,
+				     task_model_record_usage,
+				     PARSE_OPT_STOP_AT_NON_OPTION);
+
+			return task_model__record(&tmodel, argc, argv);
+	} else if (argc && !strncmp(argv[0], "rep", 3)) {
+		argc = parse_options(argc, argv, task_model_report_options,
+				     task_model_report_usage,
+				     PARSE_OPT_STOP_AT_NON_OPTION);
+
+		if ((!!tmodel.nmi + !!tmodel.irq + !!tmodel.thread) != 1) {
+			pr_err("You need to specify (just) one of following options:");
+			pr_err("-irq, --nmi or --thread\n");
+			return -1;
+		}
+
+		if (tmodel.nmi)
+			tmodel.mode = MODE_NMI;
+		else if (tmodel.irq)
+			tmodel.mode = MODE_IRQ;
+		else
+			tmodel.mode = MODE_THREAD;
+
+		if (!tmodel.pid) {
+			pr_err("You need to specify a thread PID/IRQ vector\n");
+			return -1;
+		}
+
+		automaton_create_context();
+
+		if (!open_automaton(&tmodel)) {
+			pr_err("Error while opening the automaton\n");
+			return -1;
+		}
+
+		return __cmd_task_model(&tmodel);
+	} else {
+		usage_with_options(task_model_usage, task_model_options);
+	}
+
+	setup_pager();
+
+	return -1;
+}
diff --git a/tools/perf/builtin.h b/tools/perf/builtin.h
index 05745f3ce912..fd00c3950e26 100644
--- a/tools/perf/builtin.h
+++ b/tools/perf/builtin.h
@@ -39,6 +39,7 @@ int cmd_inject(int argc, const char **argv);
 int cmd_mem(int argc, const char **argv);
 int cmd_data(int argc, const char **argv);
 int cmd_ftrace(int argc, const char **argv);
+int cmd_task_model(int argc, const char **argv);
 
 int find_scripts(char **scripts_array, char **scripts_path_array);
 #endif
diff --git a/tools/perf/perf.c b/tools/perf/perf.c
index 62b13518bc6e..3adbc066aee8 100644
--- a/tools/perf/perf.c
+++ b/tools/perf/perf.c
@@ -61,6 +61,7 @@ static struct cmd_struct commands[] = {
 	{ "bench",	cmd_bench,	0 },
 	{ "stat",	cmd_stat,	0 },
 	{ "timechart",	cmd_timechart,	0 },
+	{ "task_model",	cmd_task_model,	0 },
 	{ "top",	cmd_top,	0 },
 	{ "annotate",	cmd_annotate,	0 },
 	{ "version",	cmd_version,	0 },
