diff options
Diffstat (limited to 'public/tier0')
50 files changed, 18650 insertions, 0 deletions
diff --git a/public/tier0/EventMasks.h b/public/tier0/EventMasks.h new file mode 100644 index 0000000..0185567 --- /dev/null +++ b/public/tier0/EventMasks.h @@ -0,0 +1,480 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +// +//=============================================================================// +#pragma once + +typedef union EVENT_MASK(TC_deliver_mode) +{ + struct + { + uint16 DD:1; // both logical processors in deliver mode }, + uint16 DB:1; // logical processor 0 in deliver mode, 1 in build mode }, + uint16 DI:1; // logical processor 0 in deliver mode, 1 is inactive }, + uint16 BD:1; // logical processor 0 in build mode, 1 in deliver mode }, + uint16 BB:1; // both logical processors in build mode }, + uint16 BI:1; // logical processor 0 in build mode, 1 is inactive }, + uint16 ID:1; // logical processor 0 is inactive, 1 in deliver mode }, + uint16 IB:1; // logical processor 0 is inactive, 1 in build mode } + }; + uint16 flat; +} EVENT_MASK(TC_deliver_mode); + +typedef union EVENT_MASK(BPU_fetch_request) +{ + + struct + { + uint16 TCMISS:1; // Trace cache lookup miss }, + }; + uint16 flat; +} EVENT_MASK(BPU_fetch_request); + + +typedef union EVENT_MASK(ITLB_reference) +{ + struct + { + uint16 HIT : 1; //ITLB hit }, + uint16 MISS : 1;//ITLB miss }, + uint16 HIT_UC :1; // Uncacheable ITLB hit } + }; + uint16 flat; +} EVENT_MASK(ITLB_reference); + +typedef union EVENT_MASK(memory_cancel) +{ + struct + { + uint16 dummy : 2; + + uint16 ST_RB_FULL:1; //Replayed because no store request buffer is available }, + uint16 _64K_CONF:1; //Conflicts due to 64K aliasing } + }; + uint16 flat; +}EVENT_MASK(memory_cancel); + +typedef union EVENT_MASK(memory_complete) +{ + struct + { + uint16 LSC:1; // Load split completed, excluding UC/WC loads }, + uint16 SSC:1; //Any split stores completed } } + }; + uint16 flat; +} EVENT_MASK(memory_complete); + +typedef union EVENT_MASK(load_port_replay) +{ + struct + { + uint16 dummy:1; + uint16 SPLIT_LD:1; //Split load } } + }; + uint16 flat; +} EVENT_MASK(load_port_replay); + +typedef union EVENT_MASK(store_port_replay) +{ + struct + { + uint16 dummy0:1; + uint16 SPLIT_ST:1; //Split store } } + + }; + uint16 flat; +} EVENT_MASK(store_port_replay); + +typedef union EVENT_MASK(MOB_load_replay) +{ + struct + { + uint16 dummy0:1; + + uint16 NO_STA:1; //Replayed because of unknown store address }, + + uint16 dummy2:1; + + uint16 NO_STD:1; //Replayed because of unknown store data }, + uint16 PARTIAL_DATA:1; //Replayed because of partially overlapped data access between the load and store operations }, + uint16 UNALGN_ADDR:1; //Replayed because the lower 4 bits of the linear address do not match between the load and store operations } } + }; + uint16 flat; +}EVENT_MASK(MOB_load_replay); + +typedef union EVENT_MASK(page_walk_type) +{ + struct + { + uint16 DTMISS:1; // Page walk for a data TLB miss }, + uint16 ITMISS:1; // Page walk for an instruction TLB miss } } + }; + uint16 flat; +}EVENT_MASK(page_walk_type); + + +typedef union EVENT_MASK(BSQ_cache_reference) +{ + struct + { + uint16 RD_2ndL_HITS:1; // Read 2nd level cache hit Shared }, + uint16 RD_2ndL_HITE:1; // Read 2nd level cache hit Exclusive }, + uint16 RD_2ndL_HITM:1; // Read 2nd level cache hit Modified }, + uint16 RD_3rdL_HITS:1; // Read 3rd level cache hit Shared }, + uint16 RD_3rdL_HITE:1; // Read 3rd level cache hit Exclusive }, + uint16 RD_3rdL_HITM:1; // Read 3rd level cache hit Modified }, + uint16 dummy6:1; + uint16 dummy7:1; + uint16 RD_2ndL_MISS:1; // Read 2nd level cache miss }, + uint16 RD_3rdL_MISS:1; // Read 3rd level cache miss }, + uint16 WR_2ndL_MISS:1; // Writeback lookup from DAC misses the 2nd level cache } } + }; + uint16 flat; +} EVENT_MASK(BSQ_cache_reference) ; + +typedef union EVENT_MASK(IOQ) +{ + struct + { + uint16 bit0:1; // bus request type (use 00001 for invalid or default) + uint16 bit1:1; // + uint16 bit2:1; // + uint16 bit3:1; // + uint16 bit4:1; // + uint16 ALL_READ:1; // Count read entries }, + uint16 ALL_WRITE:1; // Count write entries }, + uint16 MEM_UC:1; // Count UC memory access entries }, + uint16 MEM_WC:1; // Count WC memory access entries }, + uint16 MEM_WT:1; // Count WT memory access entries }, + uint16 MEM_WP:1; // Count WP memory access entries }, + uint16 MEM_WB:1; // Count WB memory access entries }, + uint16 dummy12:1; + + uint16 OWN:1; // Count own store requests }, + uint16 OTHER:1; // Count other and DMA store requests }, + uint16 PREFETCH:1; // Include HW and SW prefetch requests } } + }; + uint16 flat; +} EVENT_MASK(IOQ) ; + +typedef union EVENT_MASK(FSB_data_activity) +{ + struct + { + /* DRDY_OWN is mutually exclusive with DRDY_OTHER */ + /* DBSY_OWN is mutually exclusive with DBSY_OTHER */ + uint16 DRDY_DRV:1; // Count when this processor drives data onto the bus }, + uint16 DRDY_OWN:1; // Count when this processor reads data from the bus }, + uint16 DRDY_OTHER:1; // Count when data is on the bus but not being sampled by the processor }, + uint16 DBSY_DRV:1; // Count when this processor reserves the bus for driving data }, + uint16 DBSY_OWN:1; // Count when this processor reserves the bus for sampling data }, + uint16 DBSY_OTHER:1; // Count when the bus is reserved for driving data this processor will not sample } } + }; + uint16 flat; +}EVENT_MASK(FSB_data_activity); + +typedef union EVENT_MASK(BSQ) +{ + struct + { + uint16 REQ_TYPE0:1; // Request type encoding bit 0 }, + uint16 REQ_TYPE1:1; // Request type encoding bit 1 }, + uint16 REQ_LEN0:1; // Request length encoding bit 0 }, + uint16 REQ_LEN1:1; // Request length encoding bit 1 }, + uint16 dummy4: 1; + uint16 REQ_IO_TYPE:1; // Request type is input or output }, + uint16 REQ_LOCK_TYPE:1; // Request type is bus lock }, + uint16 REQ_CACHE_TYPE:1; // Request type is cacheable }, + uint16 REQ_SPLIT_TYPE:1; // Request type is a bus 8-byte chunk split across 8-byte boundary }, + uint16 REQ_DEM_TYPE:1; // Request type is a demand (1) or prefetch (0) }, + uint16 REQ_ORD_TYPE:1; // Request is an ordered type }, + uint16 MEM_TYPE0:1; // Memory type encoding bit 0 }, + uint16 MEM_TYPE1:1; // Memory type encoding bit 1 }, + uint16 MEM_TYPE2:1; // Memory type encoding bit 2 } } + }; + uint16 flat; +} EVENT_MASK(BSQ); + +typedef union EVENT_MASK(firm_uop) +{ + struct + { + uint16 dummy15 : 15; + uint16 ALL:1; // count all uops of this type } } + }; + uint16 flat; +} EVENT_MASK(firm_uop); + + + +typedef union EVENT_MASK(TC_misc) +{ + struct + { + uint16 dymmy4 : 4; + uint16 FLUSH:1; // Number of flushes } } + }; + uint16 flat; +} EVENT_MASK(TC_misc); + +typedef union EVENT_MASK(global_power_events) +{ + struct + { + uint16 Running:1; // The processor is active } } + }; + uint16 flat; +} EVENT_MASK(global_power_events); + +typedef union EVENT_MASK(tc_ms_xfer) +{ + struct + { + uint16 CISC:1; // A TC to MS transfer ocurred } } + }; + uint16 flat; +}EVENT_MASK(tc_ms_xfer); + + + +typedef union EVENT_MASK(uop_queue_writes) +{ + struct + { + uint16 FROM_TC_BUILD:1; // uops written from TC build mode + uint16 FROM_TC_DELIVER:1; // uops written from TC deliver mode + uint16 FROM_ROM:1; // uops written from microcode ROM } } + }; + uint16 flat; +} EVENT_MASK(uop_queue_writes); + +typedef union EVENT_MASK(branch_type) +{ + struct + { + uint16 dummy : 1; + uint16 CONDITIONAL:1; // Conditional jumps + uint16 CALL:1; // Direct or indirect call + uint16 RETURN:1; // Return branches + uint16 INDIRECT:1; // Returns, indirect calls, or indirect jumps + }; + uint16 flat; +} EVENT_MASK(branch_type); + + + +typedef union EVENT_MASK(resource_stall) +{ + struct + { + uint16 dummy1 : 5; + uint16 SBFULL:1; // A Stall due to lack of store buffers } } + }; + uint16 flat; +} EVENT_MASK(resource_stall); + + + + +typedef union EVENT_MASK(WC_Buffer) +{ + struct + { + uint16 WCB_EVICTS : 1; // all causes }, + uint16 WCB_FULL_EVICT : 1; // no WC buffer is available }, + /* XXX: 245472-011 no longer lists bit 2, but that looks like + a table formatting error. Keeping it for now. */ + uint16 WCB_HITM_EVICT : 1; // store encountered a Hit Modified condition } } + }; + uint16 flat; +} EVENT_MASK(WC_Buffer); + + +typedef union EVENT_MASK(b2b_cycles) +{ + struct + { + uint16 dummy0 : 1; + uint16 bit1 : 1; // + uint16 bit2 : 1; // + uint16 bit3 : 1; // + uint16 bit4 : 1; // + uint16 bit5 : 1; // + uint16 bit6 : 1; // + + }; + uint16 flat; +} EVENT_MASK(b2b_cycles); + +typedef union EVENT_MASK(bnr) +{ + struct + { + uint16 bit0:1; // + uint16 bit1:1; // + uint16 bit2:1; // + }; + uint16 flat; +} EVENT_MASK(bnr); + + +typedef union EVENT_MASK(snoop) +{ + struct + { + uint16 dummy0 : 1; + uint16 dummy1 : 1; + + uint16 bit2:1; // + uint16 dummy3:1; // + uint16 dummy4:1; // + uint16 dummy5:1; // + uint16 bit6:1; // + uint16 bit7:1; // + }; + uint16 flat; +} EVENT_MASK(snoop); + + +typedef union EVENT_MASK(response) +{ + struct + { + uint16 dummy0:1; // + uint16 bit1:1; // + uint16 bit2:1; // + uint16 dummy3:1; // + uint16 dummy4:1; // + uint16 dummy5:1; // + uint16 dummy6:1; // + uint16 dummy7:1; // + uint16 bit8:1; // + uint16 bit9:1; // + }; + uint16 flat; +} EVENT_MASK(response); + + +typedef union EVENT_MASK(nbogus_bogus) +{ + struct + { + uint16 NBOGUS:1; // The marked uops are not bogus + uint16 BOGUS:1; // The marked uops are bogus + }; + uint16 flat; +} EVENT_MASK(nbogus_bogus); + + +typedef union EVENT_MASK(execution_event) +{ + struct + { + uint16 NBOGUS0:1; // non-bogus uops with tag bit 0 set }, + uint16 NBOGUS1:1; // non-bogus uops with tag bit 1 set }, + uint16 NBOGUS2:1; // non-bogus uops with tag bit 2 set }, + uint16 NBOGUS3:1; // non-bogus uops with tag bit 3 set }, + uint16 BOGUS0:1; // bogus uops with tag bit 0 set }, + uint16 BOGUS1:1; // bogus uops with tag bit 1 set }, + uint16 BOGUS2:1; // bogus uops with tag bit 2 set }, + uint16 BOGUS3:1; // bogus uops with tag bit 3 set } } + }; + uint16 flat; +}EVENT_MASK(execution_event); + +typedef union EVENT_MASK(instr_retired) +{ + struct + { + uint16 NBOGUSNTAG:1; // Non-bogus instructions that are not tagged }, + uint16 NBOGUSTAG:1; // Non-bogus instructions that are tagged }, + uint16 BOGUSNTAG:1; // Bogus instructions that are not tagged }, + uint16 BOGUSTAG:1; // Bogus instructions that are tagged } } + }; + uint16 flat; +} EVENT_MASK(instr_retired); + + +typedef union EVENT_MASK(uop_type) +{ + struct + { + uint16 dummy0 : 1; + uint16 TAGLOADS:1; // The uop is a load operation }, + uint16 TAGSTORES:1; // The uop is a store operation } } + }; + uint16 flat; +} EVENT_MASK(uop_type); + +typedef union EVENT_MASK(branch_retired) +{ + struct + { + uint16 MMNP:1; // Branch Not-taken Predicted + uint16 MMNM:1; // Branch Not-taken Mispredicted + uint16 MMTP:1; // Branch Taken Predicted + uint16 MMTM:1; // Branch Taken Mispredicted + }; + uint16 flat; +} EVENT_MASK(branch_retired); + +typedef union EVENT_MASK(mispred_branch_retired) +{ + struct + { + uint16 NBOGUS:1; // The retired branch is not bogus } } + }; + uint16 flat; +} EVENT_MASK(mispred_branch_retired); + + +typedef union EVENT_MASK(x87_assist) +{ + struct + { + uint16 FPSU:1; // FP stack underflow }, + uint16 FPSO:1; // FP stack overflow }, + uint16 POAO:1; // x87 output overflow }, + uint16 POAU:1; // x87 output underflow }, + uint16 PREA:1; // x87 input assist } } + }; + uint16 flat; +}EVENT_MASK(x87_assist); + +typedef union EVENT_MASK(machine_clear) +{ + struct + { + uint16 CLEAR:1; // Count a portion of the cycles when the machine is cleared }, + uint16 dummy1: 1; + uint16 MOCLEAR:1; // Count clears due to memory ordering issues }, + uint16 dummy3: 1; + uint16 dummy4: 1; + uint16 dummy5: 1; + + uint16 SMCLEAR:1;// Count clears due to self-modifying code issues } } + }; + uint16 flat; +} EVENT_MASK(machine_clear); + + +typedef union EVENT_MASK(x87_SIMD_moves_uop) +{ + struct + { + uint16 dummy3:3; + uint16 ALLP0:1; // Count all x87/SIMD store/move uops }, + uint16 ALLP2:1; // count all x87/SIMD load uops } } + }; + uint16 flat; +} EVENT_MASK(x87_SIMD_moves_uop); + + + + + + + diff --git a/public/tier0/EventModes.h b/public/tier0/EventModes.h new file mode 100644 index 0000000..b6b9756 --- /dev/null +++ b/public/tier0/EventModes.h @@ -0,0 +1,1787 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +// +//=============================================================================// +#ifndef EVENTMODES_H +#define EVENTMODES_H + + +#pragma once + +/* + + + + Event Modes to choose from: + + P4Event_TC_deliver_mode + + P4Event_BPU_fetch_request + + P4Event_ITLB_reference + + P4Event_memory_cancel + + P4Event_memory_complete + + P4Event_load_port_replay + + P4Event_store_port_replay + + P4Event_MOB_load_replay + + P4Event_page_walk_type + + P4Event_BSQ_cache_reference + + P4Event_IOQ_allocation + + P4Event_IOQ_active_entries + + P4Event_FSB_data_activity + + P4Event_BSQ_allocation + + P4Event_BSQ_active_entries + + P4Event_SSE_input_assist + + P4Event_packed_SP_uop + + P4Event_packed_DP_uop + + P4Event_scalar_SP_uop + + P4Event_scalar_DP_uop + + P4Event_64bit_MMX_uop + + P4Event_128bit_MMX_uop + + P4Event_x87_FP_uop + + P4Event_x87_SIMD_moves_uop + + P4Event_TC_misc + + P4Event_global_power_events + + P4Event_tc_ms_xfer + + P4Event_uop_queue_writes + + P4Event_retired_mispred_branch_type + + P4Event_retired_branch_type + + P4Event_resource_stall + + P4Event_WC_Buffer + + P4Event_b2b_cycles + + P4Event_bnr + + P4Event_snoop + + P4Event_response + + P4Event_front_end_event + + P4Event_execution_event + + P4Event_replay_event + + P4Event_instr_retired + + P4Event_uops_retired + + P4Event_uop_type + + P4Event_branch_retired + + P4Event_mispred_branch_retired + + P4Event_x87_assist + + P4Event_machine_clear + + +*/ + + + +class P4P4Event_TC_deliver_mode: public P4BaseEvent +{ +public: + EVENT_MASK(TC_deliver_mode) * eventMask; + + P4P4Event_TC_deliver_mode() + { + eventMask = (EVENT_MASK(TC_deliver_mode) *)&m_eventMask; + + escr.ESCREventSelect = 0x01; + cccr.CCCRSelect = 0x01; + //// eventType = EVENT_TYPE(TC_deliver_mode); + description = _T("TC_deliver_mode"); + UseCounter4(); + } + + + void UseCounter4() + { + SetCounter(4);; + } + void UseCounter5() + { + SetCounter(5); + } + void UseCounter6() + { + SetCounter(6); + } + void UseCounter7() + { + SetCounter(7); + } +}; + +class P4P4Event_BPU_fetch_request: public P4BaseEvent + +{ +public: + EVENT_MASK(BPU_fetch_request)* eventMask; + + P4P4Event_BPU_fetch_request() + { + eventMask = (EVENT_MASK(BPU_fetch_request) *)&m_eventMask; + + escr.ESCREventSelect= 0x03; + cccr.CCCRSelect= 0x00; + // eventType = EVENT_TYPE(BPU_fetch_request); + description=_T("BPU_fetch_request"); + UseCounter0(); + } + void UseCounter0() + { + SetCounter(0); + } + void UseCounter1() + { + SetCounter(1); + } + void UseCounter2() + { + SetCounter(2); + } + void UseCounter3() + { + SetCounter(3); + } + +}; +class P4P4Event_ITLB_reference: public P4BaseEvent + +{ +public: + EVENT_MASK(ITLB_reference) * eventMask; + + P4P4Event_ITLB_reference() + { + eventMask = (EVENT_MASK(ITLB_reference) *)&m_eventMask; + + escr.ESCREventSelect= 0x18; + cccr.CCCRSelect= 0x03; + // eventType=EVENT_TYPE(ITLB_reference); + description=_T("ITLB_reference"); + UseCounter0(); + } + void UseCounter0() + { + SetCounter(0); + } + void UseCounter1() + { + SetCounter(1); + } + void UseCounter2() + { + SetCounter(2); + } + void UseCounter3() + { + SetCounter(3); + } +}; +class P4Event_memory_cancel: public P4BaseEvent + +{ +public: + EVENT_MASK(memory_cancel) * eventMask; + + P4Event_memory_cancel() + { + eventMask = (EVENT_MASK(memory_cancel) *)&m_eventMask; + + escr.ESCREventSelect= 0x02; + cccr.CCCRSelect= 0x05; + // eventType=EVENT_TYPE(memory_cancel); + description=_T("memory_cancel"); + UseCounter8(); + } + + void UseCounter8() + { + SetCounter(8); + } + void UseCounter9() + { + SetCounter(9); + } + void UseCounter10() + { + SetCounter(10); + } + void UseCounter11() + { + SetCounter(11); + } + +}; +class P4Event_memory_complete: public P4BaseEvent + +{ +public: + EVENT_MASK(memory_complete) * eventMask; + + P4Event_memory_complete() + { + eventMask = (EVENT_MASK(memory_complete) *)&m_eventMask; + + escr.ESCREventSelect= 0x08; + cccr.CCCRSelect= 0x02; + // eventType=EVENT_TYPE(memory_complete); + description=_T("memory_complete"); + UseCounter8(); + } + void UseCounter8() + { + SetCounter(8); + } + void UseCounter9() + { + SetCounter(9); + } + void UseCounter10() + { + SetCounter(10); + } + void UseCounter11() + { + SetCounter(11); + } + +}; +class P4Event_load_port_replay: public P4BaseEvent + +{ +public: + EVENT_MASK(load_port_replay) * eventMask; + + P4Event_load_port_replay() + { + eventMask = (EVENT_MASK(load_port_replay) *)&m_eventMask; + + escr.ESCREventSelect= 0x04; + cccr.CCCRSelect= 0x02; + // eventType=EVENT_TYPE(load_port_replay); + description=_T("load_port_replay"); + UseCounter8(); + } + void UseCounter8() + { + SetCounter(8); + } + void UseCounter9() + { + SetCounter(9); + } + void UseCounter10() + { + SetCounter(10); + } + void UseCounter11() + { + SetCounter(11); + } + +}; +class P4Event_store_port_replay: public P4BaseEvent + +{ +public: + EVENT_MASK(store_port_replay) * eventMask; + + P4Event_store_port_replay() + { + eventMask = (EVENT_MASK(store_port_replay) *)&m_eventMask; + + escr.ESCREventSelect= 0x05; + cccr.CCCRSelect= 0x02; + // eventType=EVENT_TYPE(store_port_replay); + description=_T("store_port_replay"); + UseCounter8(); + } + void UseCounter8() + { + SetCounter(8); + } + void UseCounter9() + { + SetCounter(9); + } + void UseCounter10() + { + SetCounter(10); + } + void UseCounter11() + { + SetCounter(11); + } + +}; +class P4Event_MOB_load_replay: public P4BaseEvent + +{ +public: + EVENT_MASK(MOB_load_replay) * eventMask; + + P4Event_MOB_load_replay() + { + eventMask = (EVENT_MASK(MOB_load_replay) *)&m_eventMask; + + escr.ESCREventSelect= 0x03; + cccr.CCCRSelect= 0x02; + // eventType=EVENT_TYPE(MOB_load_replay); + description=_T("MOB_load_replay"); + UseCounter0(); + } + void UseCounter0() + { + SetCounter(0); + } + void UseCounter1() + { + SetCounter(1); + } + void UseCounter2() + { + SetCounter(2); + } + void UseCounter3() + { + SetCounter(3); + } +}; +class P4Event_page_walk_type: public P4BaseEvent + +{ +public: + EVENT_MASK(page_walk_type) * eventMask; + + P4Event_page_walk_type() + { + eventMask = (EVENT_MASK(page_walk_type) *)&m_eventMask; + + escr.ESCREventSelect= 0x01; + cccr.CCCRSelect= 0x04; + // eventType=EVENT_TYPE(page_walk_type); + description=_T("page_walk_type"); + UseCounter0(); + } + void UseCounter0() + { + SetCounter(0); + } + void UseCounter1() + { + SetCounter(1); + } + void UseCounter2() + { + SetCounter(2); + } + void UseCounter3() + { + SetCounter(3); + } +}; +class P4Event_BSQ_cache_reference: public P4BaseEvent + +{ +public: + EVENT_MASK(BSQ_cache_reference) * eventMask; + + P4Event_BSQ_cache_reference() + { + eventMask = (EVENT_MASK(BSQ_cache_reference) *)&m_eventMask; + + escr.ESCREventSelect= 0x0C; + cccr.CCCRSelect= 0x07; + // eventType=EVENT_TYPE(BSQ_cache_reference); + description=_T("BSQ_cache_reference"); + UseCounter0(); + } + void UseCounter0() + { + SetCounter(0); + } + void UseCounter1() + { + SetCounter(1); + } + void UseCounter2() + { + SetCounter(2); + } + void UseCounter3() + { + SetCounter(3); + } +}; +class P4Event_IOQ_allocation: public P4BaseEvent + +{ +public: + EVENT_MASK(IOQ) * eventMask; + + P4Event_IOQ_allocation() + { + eventMask = (EVENT_MASK(IOQ) *)&m_eventMask; + + escr.ESCREventSelect= 0x03; + cccr.CCCRSelect= 0x06; + // eventType=EVENT_TYPE(IOQ); + description=_T("IOQ_allocation"); + UseCounter0(); + } + void UseCounter0() + { + SetCounter(0); + } + void UseCounter1() + { + SetCounter(1); + } + void UseCounter2() + { + SetCounter(2); + } + void UseCounter3() + { + SetCounter(3); + } +}; +class P4Event_IOQ_active_entries: public P4BaseEvent + +{ +public: + EVENT_MASK(IOQ) * eventMask; + + P4Event_IOQ_active_entries() + { + eventMask = (EVENT_MASK(IOQ) *)&m_eventMask; + + escr.ESCREventSelect= 0x1A; + cccr.CCCRSelect= 0x06; + // eventType=EVENT_TYPE(IOQ); + description=_T("IOQ_active_entries"); + UseCounter2(); + } + void UseCounter2() + { + SetCounter(2); + } + void UseCounter3() + { + SetCounter(3); + } +}; +class P4Event_FSB_data_activity: public P4BaseEvent + +{ +public: + EVENT_MASK(FSB_data_activity) * eventMask; + + P4Event_FSB_data_activity() + { + eventMask = (EVENT_MASK(FSB_data_activity) *)&m_eventMask; + + escr.ESCREventSelect= 0x17; + cccr.CCCRSelect= 0x06; + // eventType=EVENT_TYPE(FSB_data_activity); + description=_T("FSB_data_activity"); + UseCounter0(); + } + void UseCounter0() + { + SetCounter(0); + } + void UseCounter1() + { + SetCounter(1); + } + void UseCounter2() + { + SetCounter(2); + } + void UseCounter3() + { + SetCounter(3); + } +}; +class P4Event_BSQ_allocation: public P4BaseEvent + +{ +public: + EVENT_MASK(BSQ) * eventMask; + + P4Event_BSQ_allocation() + { + eventMask = (EVENT_MASK(BSQ) *)&m_eventMask; + + escr.ESCREventSelect= 0x05; + cccr.CCCRSelect= 0x07; + // eventType=EVENT_TYPE(BSQ); + description=_T("BSQ_allocation"); + UseCounter0(); + } + + void UseCounter0() + { + SetCounter(0); + } + void UseCounter1() + { + SetCounter(1); + + } +}; +class P4Event_BSQ_active_entries: public P4BaseEvent + +{ +public: + EVENT_MASK(BSQ) * eventMask; + + P4Event_BSQ_active_entries() + { + eventMask = (EVENT_MASK(BSQ) *)&m_eventMask; + + escr.ESCREventSelect= 0x06; + cccr.CCCRSelect= 0x07; + // eventType=EVENT_TYPE(BSQ); + description=_T("bsq_active_entries"); + UseCounter2(); + } + void UseCounter2() + { + SetCounter(2); + } + void UseCounter3() + { + SetCounter(3); + } +}; +class P4Event_SSE_input_assist: public P4BaseEvent + +{ +public: + EVENT_MASK(firm_uop) * eventMask; + + P4Event_SSE_input_assist() + { + eventMask = (EVENT_MASK(firm_uop) *)&m_eventMask; + + escr.ESCREventSelect= 0x34; + cccr.CCCRSelect= 0x01; + // eventType=EVENT_TYPE(firm_uop); + description=_T("SSE_input_assist"); + UseCounter8(); + } + void UseCounter8() + { + SetCounter(8); + } + void UseCounter9() + { + SetCounter(9); + } + void UseCounter10() + { + SetCounter(10); + } + void UseCounter11() + { + SetCounter(11); + } + +}; +class P4Event_packed_SP_uop: public P4BaseEvent + +{ +public: + EVENT_MASK(firm_uop) * eventMask; + + P4Event_packed_SP_uop() + { + eventMask = (EVENT_MASK(firm_uop) *)&m_eventMask; + + escr.ESCREventSelect= 0x08; + cccr.CCCRSelect= 0x01; + // eventType=EVENT_TYPE(firm_uop); + description=_T("packed_SP_uop"); + UseCounter8(); + } + void UseCounter8() + { + SetCounter(8); + } + void UseCounter9() + { + SetCounter(9); + } + void UseCounter10() + { + SetCounter(10); + } + void UseCounter11() + { + SetCounter(11); + } + +}; +class P4Event_packed_DP_uop: public P4BaseEvent + +{ +public: + EVENT_MASK(firm_uop) * eventMask; + + P4Event_packed_DP_uop() + { + eventMask = (EVENT_MASK(firm_uop) *)&m_eventMask; + + escr.ESCREventSelect= 0x0C; + cccr.CCCRSelect= 0x01; + // eventType=EVENT_TYPE(firm_uop); + description=_T("packed_DP_uop"); + UseCounter8(); + } + void UseCounter8() + { + SetCounter(8); + } + void UseCounter9() + { + SetCounter(9); + } + void UseCounter10() + { + SetCounter(10); + } + void UseCounter11() + { + SetCounter(11); + } + +}; +class P4Event_scalar_SP_uop: public P4BaseEvent + +{ +public: + EVENT_MASK(firm_uop) * eventMask; + + P4Event_scalar_SP_uop() + { + eventMask = (EVENT_MASK(firm_uop) *)&m_eventMask; + + escr.ESCREventSelect= 0x0A; + cccr.CCCRSelect= 0x01; + // eventType=EVENT_TYPE(firm_uop); + description=_T("scalar_SP_uop"); + UseCounter8(); + } + void UseCounter8() + { + SetCounter(8); + } + void UseCounter9() + { + SetCounter(9); + } + void UseCounter10() + { + SetCounter(10); + } + void UseCounter11() + { + SetCounter(11); + } + +}; +class P4Event_scalar_DP_uop: public P4BaseEvent + +{ +public: + EVENT_MASK(firm_uop) * eventMask; + + P4Event_scalar_DP_uop() + { + eventMask = (EVENT_MASK(firm_uop) *)&m_eventMask; + + escr.ESCREventSelect= 0x0E; + cccr.CCCRSelect= 0x01; + // eventType=EVENT_TYPE(firm_uop); + description=_T("scalar_DP_uop"); + UseCounter8(); + } + void UseCounter8() + { + SetCounter(8); + } + void UseCounter9() + { + SetCounter(9); + } + void UseCounter10() + { + SetCounter(10); + } + void UseCounter11() + { + SetCounter(11); + } + +}; +class P4Event_64bit_MMX_uop: public P4BaseEvent + +{ +public: + EVENT_MASK(firm_uop) * eventMask; + + P4Event_64bit_MMX_uop() + { + eventMask = (EVENT_MASK(firm_uop) *)&m_eventMask; + + escr.ESCREventSelect= 0x02; + cccr.CCCRSelect= 0x01; + // eventType=EVENT_TYPE(firm_uop); + description=_T("64bit_MMX_uop"); + UseCounter8(); + } + void UseCounter8() + { + SetCounter(8); + } + void UseCounter9() + { + SetCounter(9); + } + void UseCounter10() + { + SetCounter(10); + } + void UseCounter11() + { + SetCounter(11); + } + +}; +class P4Event_128bit_MMX_uop: public P4BaseEvent + +{ +public: + EVENT_MASK(firm_uop) * eventMask; + + P4Event_128bit_MMX_uop() + { + eventMask = (EVENT_MASK(firm_uop) *)&m_eventMask; + + escr.ESCREventSelect= 0x1A; + cccr.CCCRSelect= 0x01; + // eventType=EVENT_TYPE(firm_uop); + description=_T("128bit_MMX_uop"); + UseCounter8(); + } + void UseCounter8() + { + SetCounter(8); + } + void UseCounter9() + { + SetCounter(9); + } + void UseCounter10() + { + SetCounter(10); + } + void UseCounter11() + { + SetCounter(11); + } + +}; +class P4Event_x87_FP_uop: public P4BaseEvent + +{ +public: + EVENT_MASK(firm_uop) * eventMask; + + P4Event_x87_FP_uop() + { + eventMask = (EVENT_MASK(firm_uop) *)&m_eventMask; + + escr.ESCREventSelect= 0x04; + cccr.CCCRSelect= 0x01; + // eventType=EVENT_TYPE(firm_uop); + description=_T("x87_FP_uop"); + UseCounter8(); + } + void UseCounter8() + { + SetCounter(8); + } + void UseCounter9() + { + SetCounter(9); + } + void UseCounter10() + { + SetCounter(10); + } + void UseCounter11() + { + SetCounter(11); + } + +}; +class P4Event_x87_SIMD_moves_uop: public P4BaseEvent + +{ +public: + EVENT_MASK(x87_SIMD_moves_uop) * eventMask; + + P4Event_x87_SIMD_moves_uop() + { + eventMask = (EVENT_MASK(x87_SIMD_moves_uop) *)&m_eventMask; + + escr.ESCREventSelect= 0x2E; + cccr.CCCRSelect= 0; + // eventType=EVENT_TYPE(x87_SIMD_moves_uop); + description=_T("x87_SIMD_moves_uop"); + UseCounter8(); + } + void UseCounter8() + { + SetCounter(8); + } + void UseCounter9() + { + SetCounter(9); + } + void UseCounter10() + { + SetCounter(10); + } + void UseCounter11() + { + SetCounter(11); + } + +}; +class P4Event_TC_misc: public P4BaseEvent + +{ +public: + EVENT_MASK(TC_misc) * eventMask; + + P4Event_TC_misc() + { + eventMask = (EVENT_MASK(TC_misc) *)&m_eventMask; + + escr.ESCREventSelect= 0x06; + cccr.CCCRSelect= 0x01; + // eventType=EVENT_TYPE(TC_misc); + description=_T("TC_misc"); + UseCounter4(); + } + + void UseCounter4() + { + SetCounter(4);; + } + void UseCounter5() + { + SetCounter(5); + } + void UseCounter6() + { + SetCounter(6); + } + void UseCounter7() + { + SetCounter(7); + } +}; +class P4Event_global_power_events: public P4BaseEvent + +{ +public: + EVENT_MASK(global_power_events) * eventMask; + + P4Event_global_power_events() + { + eventMask = (EVENT_MASK(global_power_events) *)&m_eventMask; + + escr.ESCREventSelect= 0x13; + cccr.CCCRSelect= 0x06; + // eventType=EVENT_TYPE(global_power_events); + description=_T("global_power_events"); + UseCounter0(); + } + + void UseCounter0() + { + SetCounter(0); + } + void UseCounter1() + { + SetCounter(1); + } + void UseCounter2() + { + SetCounter(2); + } + void UseCounter3() + { + SetCounter(3); + } +}; +class P4Event_tc_ms_xfer: public P4BaseEvent + +{ +public: + EVENT_MASK(tc_ms_xfer) * eventMask; + + P4Event_tc_ms_xfer() + { + eventMask = (EVENT_MASK(tc_ms_xfer) *)&m_eventMask; + + escr.ESCREventSelect= 0x05; + cccr.CCCRSelect= 0x00; + // eventType=EVENT_TYPE(tc_ms_xfer); + description=_T("tc_ms_xfer"); + UseCounter4(); + } + + void UseCounter4() + { + SetCounter(4);; + } + void UseCounter5() + { + SetCounter(5); + } + void UseCounter6() + { + SetCounter(6); + } + void UseCounter7() + { + SetCounter(7); + } +}; +class P4Event_uop_queue_writes: public P4BaseEvent + +{ +public: + EVENT_MASK(uop_queue_writes) * eventMask; + + P4Event_uop_queue_writes() + { + eventMask = (EVENT_MASK(uop_queue_writes) *)&m_eventMask; + + escr.ESCREventSelect= 0x09; + cccr.CCCRSelect= 0x00; + // eventType=EVENT_TYPE(uop_queue_writes); + description=_T("uop_queue_writes"); + UseCounter4(); + } + + void UseCounter4() + { + SetCounter(4);; + } + void UseCounter5() + { + SetCounter(5); + } + void UseCounter6() + { + SetCounter(6); + } + void UseCounter7() + { + SetCounter(7); + } +}; +class P4Event_retired_mispred_branch_type: public P4BaseEvent + +{ +public: + EVENT_MASK(branch_type) * eventMask; + + P4Event_retired_mispred_branch_type() + { + eventMask = (EVENT_MASK(branch_type) *)&m_eventMask; + + escr.ESCREventSelect= 0x05; + cccr.CCCRSelect= 0x02; + // eventType=EVENT_TYPE(branch_type); + description=_T("retired_mispred_branch_type"); + UseCounter4(); + } + + void UseCounter4() + { + SetCounter(4);; + } + void UseCounter5() + { + SetCounter(5); + } + void UseCounter6() + { + SetCounter(6); + } + void UseCounter7() + { + SetCounter(7); + } +}; +class P4Event_retired_branch_type: public P4BaseEvent + +{ +public: + EVENT_MASK(branch_type) * eventMask; + + P4Event_retired_branch_type() + { + eventMask = (EVENT_MASK(branch_type) *)&m_eventMask; + + escr.ESCREventSelect= 0x04; + cccr.CCCRSelect= 0x04; + // eventType=EVENT_TYPE(branch_type); + description=_T("retired_branch_type"); + UseCounter4(); + } + + void UseCounter4() + { + SetCounter(4);; + } + void UseCounter5() + { + SetCounter(5); + } + void UseCounter6() + { + SetCounter(6); + } + void UseCounter7() + { + SetCounter(7); + } +}; +class P4Event_resource_stall: public P4BaseEvent + +{ +public: + EVENT_MASK(resource_stall) * eventMask; + + P4Event_resource_stall() + { + eventMask = (EVENT_MASK(resource_stall) *)&m_eventMask; + + escr.ESCREventSelect= 0x01; + cccr.CCCRSelect= 0x02; + // eventType=EVENT_TYPE(resource_stall); + description=_T("resource_stall"); + UseCounter12(); + } + void UseCounter12() + { + SetCounter(12); + } + void UseCounter13() + { + SetCounter(13); + + } + + void UseCounter14() + { + SetCounter(14); + } + void UseCounter15() + { + SetCounter(15); + + } + + void UseCounter16() + { + SetCounter(16); + } + void UseCounter17() + { + SetCounter(17); + + } + +}; +class P4Event_WC_Buffer: public P4BaseEvent + +{ +public: + EVENT_MASK(WC_Buffer) * eventMask; + + P4Event_WC_Buffer() + { + eventMask = (EVENT_MASK(WC_Buffer) *)&m_eventMask; + + escr.ESCREventSelect= 0x05; + cccr.CCCRSelect= 0x05; + // eventType=EVENT_TYPE(WC_Buffer); + description=_T("WC_Buffer"); + UseCounter8(); + } + void UseCounter8() + { + SetCounter(8); + } + void UseCounter9() + { + SetCounter(9); + } + void UseCounter10() + { + SetCounter(10); + } + void UseCounter11() + { + SetCounter(11); + } + +}; +class P4Event_b2b_cycles: public P4BaseEvent + +{ +public: + EVENT_MASK(b2b_cycles) * eventMask; + + P4Event_b2b_cycles() + { + eventMask = (EVENT_MASK(b2b_cycles) *)&m_eventMask; + + escr.ESCREventSelect= 0x16; + cccr.CCCRSelect= 0x03; + // eventType=EVENT_TYPE(b2b_cycles); + description=_T("b2b_cycles"); + UseCounter0(); + } + void UseCounter0() + { + SetCounter(0); + } + void UseCounter1() + { + SetCounter(1); + } + void UseCounter2() + { + SetCounter(2); + } + void UseCounter3() + { + SetCounter(3); + } +}; +class P4Event_bnr: public P4BaseEvent + +{ +public: + EVENT_MASK(bnr) * eventMask; + + P4Event_bnr() + { + eventMask = (EVENT_MASK(bnr) *)&m_eventMask; + + escr.ESCREventSelect= 0x08; + cccr.CCCRSelect= 0x03; + // eventType=EVENT_TYPE(bnr); + description=_T("bnr"); + UseCounter0(); + } + void UseCounter0() + { + SetCounter(0); + } + void UseCounter1() + { + SetCounter(1); + } + void UseCounter2() + { + SetCounter(2); + } + void UseCounter3() + { + SetCounter(3); + } +}; +class P4Event_snoop: public P4BaseEvent + +{ +public: + EVENT_MASK(snoop) * eventMask; + + P4Event_snoop() + { + eventMask = (EVENT_MASK(snoop) *)&m_eventMask; + + escr.ESCREventSelect= 0x06; + cccr.CCCRSelect= 0x03; + // eventType=EVENT_TYPE(snoop); + description=_T("snoop"); + UseCounter0(); + } + void UseCounter0() + { + SetCounter(0); + } + void UseCounter1() + { + SetCounter(1); + } + void UseCounter2() + { + SetCounter(2); + } + void UseCounter3() + { + SetCounter(3); + } +}; +class P4Event_response: public P4BaseEvent + +{ +public: + EVENT_MASK(response) * eventMask; + + P4Event_response() + { + eventMask = (EVENT_MASK(response) *)&m_eventMask; + + escr.ESCREventSelect= 0x04; + cccr.CCCRSelect= 0x03; + // eventType=EVENT_TYPE(response); + description=_T("response"); + UseCounter0(); + } + void UseCounter0() + { + SetCounter(0); + } + void UseCounter1() + { + SetCounter(1); + } + void UseCounter2() + { + SetCounter(2); + } + void UseCounter3() + { + SetCounter(3); + } +}; +class P4Event_front_end_event: public P4BaseEvent + +{ +public: + EVENT_MASK(nbogus_bogus) * eventMask; + + P4Event_front_end_event() + { + eventMask = (EVENT_MASK(nbogus_bogus) *)&m_eventMask; + + escr.ESCREventSelect= 0x08; + cccr.CCCRSelect= 0x05; + // eventType=EVENT_TYPE(nbogus_bogus); + description=_T("front_end_event"); + UseCounter12(); + } + + void UseCounter12() + { + SetCounter(12); + } + void UseCounter13() + { + SetCounter(13); + + } + + void UseCounter14() + { + SetCounter(14); + } + void UseCounter15() + { + SetCounter(15); + + } + + void UseCounter16() + { + SetCounter(16); + } + void UseCounter17() + { + SetCounter(17); + + } +}; +class P4Event_execution_event: public P4BaseEvent + +{ +public: + EVENT_MASK(execution_event) * eventMask; + + P4Event_execution_event() + { + eventMask = (EVENT_MASK(execution_event) *)&m_eventMask; + + escr.ESCREventSelect= 0x0C; + cccr.CCCRSelect= 0x05; + // eventType=EVENT_TYPE(execution_event); + description=_T("execution_event"); + UseCounter12(); + } + void UseCounter12() + { + SetCounter(12); + } + void UseCounter13() + { + SetCounter(13); + + } + + void UseCounter14() + { + SetCounter(14); + } + void UseCounter15() + { + SetCounter(15); + + } + + void UseCounter16() + { + SetCounter(16); + } + void UseCounter17() + { + SetCounter(17); + + } +}; +class P4Event_replay_event: public P4BaseEvent + +{ +public: + EVENT_MASK(nbogus_bogus) * eventMask; + + P4Event_replay_event() + { + eventMask = (EVENT_MASK(nbogus_bogus) *)&m_eventMask; + + escr.ESCREventSelect= 0x09; + cccr.CCCRSelect= 0x05; + // eventType=EVENT_TYPE(nbogus_bogus); + description=_T("replay_event"); + UseCounter12(); + } + void UseCounter12() + { + SetCounter(12); + } + void UseCounter13() + { + SetCounter(13); + + } + + void UseCounter14() + { + SetCounter(14); + } + void UseCounter15() + { + SetCounter(15); + + } + + void UseCounter16() + { + SetCounter(16); + } + void UseCounter17() + { + SetCounter(17); + + } +}; +class P4Event_instr_retired: public P4BaseEvent + +{ +public: + EVENT_MASK(instr_retired) * eventMask; + + P4Event_instr_retired() + { + eventMask = (EVENT_MASK(instr_retired) *)&m_eventMask; + + escr.ESCREventSelect= 0x02; + cccr.CCCRSelect= 0x04; + // eventType=EVENT_TYPE(instr_retired); + description=_T("instr_retired"); + UseCounter12(); + } + + void UseCounter12() + { + SetCounter(12); + } + void UseCounter13() + { + SetCounter(13); + + } + + void UseCounter14() + { + SetCounter(14); + } + void UseCounter15() + { + SetCounter(15); + + } + + void UseCounter16() + { + SetCounter(16); + } + void UseCounter17() + { + SetCounter(17); + + } +}; +class P4Event_uops_retired: public P4BaseEvent + +{ +public: + EVENT_MASK(nbogus_bogus) * eventMask; + + P4Event_uops_retired() + { + eventMask = (EVENT_MASK(nbogus_bogus) *)&m_eventMask; + + escr.ESCREventSelect= 0x01; + cccr.CCCRSelect= 0x04; + // eventType=EVENT_TYPE(nbogus_bogus); + description=_T("uops_retired"); + UseCounter12(); + } + void UseCounter12() + { + SetCounter(12); + } + void UseCounter13() + { + SetCounter(13); + + } + + void UseCounter14() + { + SetCounter(14); + } + void UseCounter15() + { + SetCounter(15); + + } + + void UseCounter16() + { + SetCounter(16); + } + void UseCounter17() + { + SetCounter(17); + + } +}; +class P4Event_uop_type: public P4BaseEvent + +{ +public: + EVENT_MASK(uop_type) * eventMask; + + P4Event_uop_type() + { + eventMask = (EVENT_MASK(uop_type) *)&m_eventMask; + + escr.ESCREventSelect= 0x02; + cccr.CCCRSelect= 0x02; + // eventType=EVENT_TYPE(uop_type); + description=_T("uop_type"); + UseCounter12(); + } + void UseCounter12() + { + SetCounter(12); + } + void UseCounter13() + { + SetCounter(13); + + } + + void UseCounter14() + { + SetCounter(14); + } + void UseCounter15() + { + SetCounter(15); + + } + + void UseCounter16() + { + SetCounter(16); + } + void UseCounter17() + { + SetCounter(17); + + } +}; +class P4Event_branch_retired: public P4BaseEvent + +{ +public: + EVENT_MASK(branch_retired) * eventMask; + + P4Event_branch_retired() + { + eventMask = (EVENT_MASK(branch_retired) *)&m_eventMask; + + escr.ESCREventSelect= 0x06; + cccr.CCCRSelect= 0x05; + // eventType=EVENT_TYPE(branch_retired); + description=_T("branch_retired"); + UseCounter12(); + } + void UseCounter12() + { + SetCounter(12); + } + void UseCounter13() + { + SetCounter(13); + + } + + void UseCounter14() + { + SetCounter(14); + } + void UseCounter15() + { + SetCounter(15); + + } + + void UseCounter16() + { + SetCounter(16); + } + void UseCounter17() + { + SetCounter(17); + + } +}; +class P4Event_mispred_branch_retired: public P4BaseEvent + +{ +public: + EVENT_MASK(mispred_branch_retired) * eventMask; + + P4Event_mispred_branch_retired() + { + eventMask = (EVENT_MASK(mispred_branch_retired) *)&m_eventMask; + + escr.ESCREventSelect= 0x03; + cccr.CCCRSelect= 0x04; + // eventType=EVENT_TYPE(mispred_branch_retired); + description=_T("mispred_branch_retired"); + UseCounter12(); + } + void UseCounter12() + { + SetCounter(12); + } + void UseCounter13() + { + SetCounter(13); + + } + + void UseCounter14() + { + SetCounter(14); + } + void UseCounter15() + { + SetCounter(15); + + } + + void UseCounter16() + { + SetCounter(16); + } + void UseCounter17() + { + SetCounter(17); + + } +}; +class P4Event_x87_assist: public P4BaseEvent + +{ +public: + EVENT_MASK(x87_assist) * eventMask; + + P4Event_x87_assist() + { + eventMask = (EVENT_MASK(x87_assist) *)&m_eventMask; + + escr.ESCREventSelect= 0x03; + cccr.CCCRSelect= 0x05; + // eventType=EVENT_TYPE(x87_assist); + description=_T("x87_assist"); + UseCounter12(); + } + void UseCounter12() + { + SetCounter(12); + } + void UseCounter13() + { + SetCounter(13); + + } + + void UseCounter14() + { + SetCounter(14); + } + void UseCounter15() + { + SetCounter(15); + + } + + void UseCounter16() + { + SetCounter(16); + } + void UseCounter17() + { + SetCounter(17); + + } +}; +class P4Event_machine_clear: public P4BaseEvent + +{ +public: + EVENT_MASK(machine_clear) * eventMask; + + P4Event_machine_clear() + { + eventMask = (EVENT_MASK(machine_clear) *)&m_eventMask; + escr.ESCREventSelect= 0x02; + cccr.CCCRSelect= 0x05; + // eventType=EVENT_TYPE(machine_clear); + description=_T("machine_clear"); + UseCounter12(); + } + + + + void UseCounter12() + { + SetCounter(12); + } + void UseCounter13() + { + SetCounter(13); + + } + + void UseCounter14() + { + SetCounter(14); + } + void UseCounter15() + { + SetCounter(15); + + } + + void UseCounter16() + { + SetCounter(16); + } + void UseCounter17() + { + SetCounter(17); + + } + +}; + +#endif // EVENTMODES_H diff --git a/public/tier0/IOCTLCodes.h b/public/tier0/IOCTLCodes.h new file mode 100644 index 0000000..a8f50d0 --- /dev/null +++ b/public/tier0/IOCTLCodes.h @@ -0,0 +1,29 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +// +//=============================================================================// +#ifndef IOCTLCODES_H +#define IOCTLCODES_H +#pragma once + +// Define the IOCTL codes we will use. The IOCTL code contains a command +// identifier, plus other information about the device, the type of access +// with which the file must have been opened, and the type of buffering. + +// Device type -- in the "User Defined" range." +#define DEVICE_FILE_TYPE 40000 + + +// The IOCTL function codes from 0x800 to 0xFFF are for customer use. + +#define IOCTL_WRITE_MSR \ + CTL_CODE( DEVICE_FILE_TYPE, 0x900, METHOD_BUFFERED, FILE_READ_ACCESS ) + +#define IOCTL_READ_MSR \ + CTL_CODE( DEVICE_FILE_TYPE, 0x901, METHOD_BUFFERED, FILE_READ_ACCESS ) + + +#endif IOCTLCODES_H diff --git a/public/tier0/K8PerformanceCounters.h b/public/tier0/K8PerformanceCounters.h new file mode 100644 index 0000000..4367dff --- /dev/null +++ b/public/tier0/K8PerformanceCounters.h @@ -0,0 +1,2032 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +// +//=============================================================================// +#ifndef K8PERFORMANCECOUNTERS_H +#define K8PERFORMANCECOUNTERS_H + +/* + * AMD K8 events. + * + */ + + + +typedef union EVENT_MASK(NULL_MASK) +{ + // no tests defined + uint16 flat; +} EVENT_MASK(NULL_MASK); + + +#define MSR_K8_EVNTSEL0 0xC0010000 /* .. 0xC0010003 */ +#define MSR_K8_PERFCTR0 0xC0010004 /* .. 0xC0010007 */ + +# pragma pack(push, 1) + + + +// access to these bits is through the methods +typedef union PerfEvtSel +{ + struct + { + uint64 EventMask : 8; + + uint64 UnitMask : 8; + uint64 USR : 1; + uint64 OS : 1; + uint64 Edge : 1; + uint64 PC : 1; + uint64 INTAPIC : 1; + uint64 Reserved21 : 1; + uint64 Enable : 1; + uint64 Complement : 1; // aka INV + uint64 Threshold : 8; // aka CounterMask + uint64 Reserver32 : 32; + }; + uint64 flat; + +} PerfEvtSel; + + +enum UnitEncode +{ + FP, + LS, + DC, + BU, + IC, + UE_Unknown, + FR, + NB +}; + +# pragma pack(pop) + +// Turn off the no return value warning in ReadCounter. +#pragma warning( disable : 4035 ) +#define k8NUM_COUNTERS 4 +class k8BaseEvent +{ +public: + + PME * pme; + + PerfEvtSel eventSelect[k8NUM_COUNTERS]; + + unsigned short m_eventMask; + int event_id; + tchar * name; + tchar revRequired; + int eventSelectNum; + UnitEncode unitEncode; + + + void SetCounter(int n) + { + if (n < 0) + n = 0; + else if (n > 3) + n = 3; + eventSelectNum = n; + + } + k8BaseEvent() + { + pme = PME::Instance(); + + for(int i = 0; i< k8NUM_COUNTERS; i++) + { + eventSelect[i].flat = 0; + + } + eventSelectNum = 0; + + m_eventMask = 0; + event_id = 0; + name = 0; + revRequired = 'A'; + + + } + + void SetCaptureMode(PrivilegeCapture priv) + { + PerfEvtSel & select = eventSelect[eventSelectNum]; + StopCounter(); + + switch (priv) + { + case OS_Only: + select.USR = 0; + select.OS = 1; + break; + + case USR_Only: + select.USR = 1; + select.OS = 0; + break; + + case OS_and_USR: + select.USR = 1; + select.OS = 1; + break; + } + + + select.UnitMask = m_eventMask; + select.EventMask = event_id; + + + int selectPort = MSR_K8_EVNTSEL0 + eventSelectNum; + pme->WriteMSR(selectPort, select.flat); + } + + + void SetFiltering(CompareState compareEnable, + CompareMethod compareMethod, + uint8 threshold, + EdgeState edgeEnable) + { + + PerfEvtSel & select = eventSelect[eventSelectNum]; + + StopCounter(); + + if (compareEnable == CompareDisable) + select.Threshold = 0; + else + select.Threshold = threshold; + + select.Complement = compareMethod; + + select.Edge = edgeEnable; + + int selectPort = MSR_K8_EVNTSEL0 + eventSelectNum; + pme->WriteMSR(selectPort, select.flat); + + + } + + + void StartCounter() + { + PerfEvtSel & select = eventSelect[eventSelectNum]; + + select.Enable = 1; + int selectPort = MSR_K8_EVNTSEL0 + eventSelectNum; + + pme->WriteMSR(selectPort, select.flat); + + } + void StopCounter() + { + PerfEvtSel & select = eventSelect[eventSelectNum]; + select.Enable = 0; + int selectPort = MSR_K8_EVNTSEL0 + eventSelectNum; + + pme->WriteMSR(selectPort, select.flat); + } + + + + void ClearCounter() + { + PerfEvtSel & select = eventSelect[eventSelectNum]; + + int counterPort = MSR_K8_PERFCTR0 + eventSelectNum; + + pme->WriteMSR(counterPort, 0ui64 ); // clear + } + + void WriteCounter(int64 value) + { + + PerfEvtSel & select = eventSelect[eventSelectNum]; + + int counterPort = MSR_K8_PERFCTR0 + eventSelectNum; + pme->WriteMSR(counterPort, value); // clear + } + + int64 ReadCounter() + { + +#if PME_DEBUG + PerfEvtSel & select = eventSelect[eventSelectNum]; + + if (select.USR == 0 && select.OS == 0) + return -1; // no area to collect, use SetCaptureMode + + if (select.EventMask == 0) + return -2; // no event mask set + + if (eventSelectNum < 0 || eventSelectNum > 3) + return -3; // counter not legal + + // check revision + +#endif + + // ReadMSR should work here too, but RDPMC should be faster + //ReadMSR(counterPort, int64); + + // we need to copy this into a temp for some reason +#ifdef COMPILER_MSVC64 + return __readpmc((unsigned long) eventSelectNum); +#else + int temp = eventSelectNum; + _asm + { + mov ecx, temp + RDPMC + } +#endif + + } + + +}; +#pragma warning( default : 4035 ) + + + + +typedef union EVENT_MASK(k8_dispatched_fpu_ops) +{ + // event 0 + struct + { + uint16 AddPipeOps:1; // Add pipe ops excluding junk ops" }, + uint16 MulPipeOps:1; // Multiply pipe ops excluding junk ops" },, + uint16 StoreOps:1; // Store pipe ops excluding junk ops" }, + uint16 AndPipeOpsJunk:1; // Add pipe junk ops" },, + uint16 MulPipeOpsJunk:1; // Multiply pipe junk ops" }, + uint16 StoreOpsJunk:1; // Store pipe junk ops" } } + }; + uint16 flat; +} EVENT_MASK(k8_dispatched_fpu_ops); + +class k8Event_DISPATCHED_FPU_OPS : public k8BaseEvent +{ +public: + + k8Event_DISPATCHED_FPU_OPS() + { + eventMask = (EVENT_MASK(k8_dispatched_fpu_ops) *)&m_eventMask; + + event_id = 0x00; + unitEncode = FP; + name = _T("Dispatched FPU ops"); + revRequired = 'B'; + } + EVENT_MASK(k8_dispatched_fpu_ops) * eventMask; + +}; + +////////////////////////////////////////////////////////// + + + +class k8Event_NO_FPU_OPS : public k8BaseEvent +{ +public: + + k8Event_NO_FPU_OPS() + { + eventMask = (EVENT_MASK(NULL_MASK) *)&m_eventMask; + event_id = 0x01; + unitEncode = FP; + + name = _T("Cycles with no FPU ops retired"); + revRequired = 'B'; + } + EVENT_MASK(NULL_MASK) * eventMask; + +}; + +////////////////////////////////////////////////////////// + +class k8Event_FAST_FPU_OPS : public k8BaseEvent +{ +public: + + k8Event_FAST_FPU_OPS() + { + eventMask = (EVENT_MASK(NULL_MASK) *)&m_eventMask; + event_id = 0x02; + unitEncode = FP; + + name = _T("Dispatched FPU ops that use the fast flag interface"); + revRequired = 'B'; + + } + EVENT_MASK(NULL_MASK) * eventMask; + +}; + + +////////////////////////////////////////////////////////// + + +typedef union EVENT_MASK(k8_segment_register_load) +{ + + struct + { + uint16 ES:1; + uint16 CS:1; + uint16 SS:1; + uint16 DS:1; + uint16 FS:1; + uint16 GS:1; + uint16 HS:1; + }; + uint16 flat; +} EVENT_MASK(k8_segment_register_load); + + +class k8Event_SEG_REG_LOAD : public k8BaseEvent +{ +public: + + k8Event_SEG_REG_LOAD() + { + eventMask = (EVENT_MASK(k8_segment_register_load) *)&m_eventMask; + name = _T("Segment register load"); + event_id = 0x20; + unitEncode = LS; + + } + EVENT_MASK(k8_segment_register_load) * eventMask; + +}; + + +class k8Event_SELF_MODIFY_RESYNC : public k8BaseEvent +{ +public: + + k8Event_SELF_MODIFY_RESYNC() + { + eventMask = (EVENT_MASK(NULL_MASK) *)&m_eventMask; + name = _T("Microarchitectural resync caused by self modifying code"); + event_id = 0x21; + unitEncode = LS; + + } + EVENT_MASK(NULL_MASK) * eventMask; + + +}; +class k8Event_LS_RESYNC_BY_SNOOP : public k8BaseEvent +{ +public: + + k8Event_LS_RESYNC_BY_SNOOP() + { + eventMask = (EVENT_MASK(NULL_MASK) *)&m_eventMask; + event_id = 0x22; + unitEncode = LS; + + name = _T("Microarchitectural resync caused by snoop"); + } + EVENT_MASK(NULL_MASK) * eventMask; + + +}; +class k8Event_LS_BUFFER_FULL : public k8BaseEvent +{ +public: + + k8Event_LS_BUFFER_FULL() + { + eventMask = (EVENT_MASK(NULL_MASK) *)&m_eventMask; + name = _T("LS Buffer 2 Full"); + event_id = 0x23; + unitEncode = LS; + + } + EVENT_MASK(NULL_MASK) * eventMask; + +}; + + +typedef union EVENT_MASK(k8_locked_op) +{ + + + struct + { + uint16 NumLockInstr : 1; //Number of lock instructions executed + uint16 NumCyclesInRequestGrant : 1; //Number of cycles spent in the lock request/grant stage + + uint16 NumCyclesForLock:1; + /*Number of cycles a lock takes to complete once it is + non-speculative and is the oldest load/store operation + (non-speculative cycles in Ls2 entry 0)*/ + + + }; + uint16 flat; + + +} EVENT_MASK(k8_locked_op); + + + +class k8Event_LOCKED_OP : public k8BaseEvent +{ +public: + + EVENT_MASK(k8_locked_op) * eventMask; + + k8Event_LOCKED_OP() + { + eventMask = (EVENT_MASK(k8_locked_op) *)&m_eventMask; + name = _T("Locked operation"); + event_id = 0x24; + unitEncode = LS; + + revRequired = 'C'; + } + + +}; + +class k8Event_OP_LATE_CANCEL : public k8BaseEvent +{ +public: + + k8Event_OP_LATE_CANCEL() + { + eventMask = (EVENT_MASK(NULL_MASK) *)&m_eventMask; + name = _T("Microarchitectural late cancel of an operation"); + event_id = 0x25; + unitEncode = LS; + + } + EVENT_MASK(NULL_MASK) * eventMask; + // name = _T("OP_LATE_CANCEL"); + + +}; +class k8Event_CFLUSH_RETIRED : public k8BaseEvent +{ +public: + + k8Event_CFLUSH_RETIRED() + { + eventMask = (EVENT_MASK(NULL_MASK) *)&m_eventMask; + name = _T("Retired CFLUSH instructions"); + event_id = 0x26; + unitEncode = LS; + + } + EVENT_MASK(NULL_MASK) * eventMask; + // name = _T("CFLUSH_RETIRED"); + + +}; +class k8Event_CPUID_RETIRED : public k8BaseEvent +{ +public: + + k8Event_CPUID_RETIRED() + { + eventMask = (EVENT_MASK(NULL_MASK) *)&m_eventMask; + name = _T("Retired CPUID instructions"); + event_id = 0x27; + unitEncode = LS; + + } + EVENT_MASK(NULL_MASK) * eventMask; + // name = _T("CPUID_RETIRED"); + + +}; + +typedef union EVENT_MASK( k8_cache) +{ + + struct + { + uint16 Invalid:1; + uint16 Exclusive:1; + uint16 Shared:1; + uint16 Owner:1; + uint16 Modified:1; + }; + uint16 flat; + +}EVENT_MASK( k8_cache); + /* 0x40-0x47: from K7 official event set */ + + +class k8Event_DATA_CACHE_ACCESSES : public k8BaseEvent +{ + k8Event_DATA_CACHE_ACCESSES() + { + + event_id = 0x40; + unitEncode = DC; + + eventMask = (EVENT_MASK(NULL_MASK) *)&m_eventMask; + + //_T("DATA_CACHE_ACCESSES"), + name = _T("Data cache accesses"); + } + EVENT_MASK(NULL_MASK) * eventMask; + +}; + +class k8Event_DATA_CACHE_MISSES : public k8BaseEvent +{ + k8Event_DATA_CACHE_MISSES() + { + + event_id = 0x41; + unitEncode = DC; + + eventMask = (EVENT_MASK(NULL_MASK) *)&m_eventMask; + + //_T("DATA_CACHE_MISSES"), + name = _T("Data cache misses"); + } + EVENT_MASK(NULL_MASK) * eventMask; +}; + +class k8Event_DATA_CACHE_REFILLS_FROM_L2 : public k8BaseEvent +{ + k8Event_DATA_CACHE_REFILLS_FROM_L2() + { + + event_id = 0x42; + unitEncode = DC; + + eventMask = (EVENT_MASK(k8_cache) *)&m_eventMask; + + + name = _T("Data cache refills from L2"); + } + EVENT_MASK(k8_cache) * eventMask; + +}; + +class k8Event_DATA_CACHE_REFILLS_FROM_SYSTEM : public k8BaseEvent +{ + k8Event_DATA_CACHE_REFILLS_FROM_SYSTEM() + { + + event_id = 0x43; + unitEncode = DC; + + + eventMask = (EVENT_MASK(k8_cache) *)&m_eventMask; + + //UM(k7_um_moesi), + //_T("DATA_CACHE_REFILLS_FROM_SYSTEM"), + name = _T("Data cache refills from system"); + } + EVENT_MASK(k8_cache) * eventMask; + +}; + +class k8Event_DATA_CACHE_WRITEBACKS : public k8BaseEvent +{ + k8Event_DATA_CACHE_WRITEBACKS() + { + + event_id = 0x44; + unitEncode = DC; + + eventMask = (EVENT_MASK(k8_cache) *)&m_eventMask; + + //UM(k7_um_moesi), + //_T("DATA_CACHE_WRITEBACKS"), + name = _T("Data cache writebacks"); + } + EVENT_MASK(k8_cache) * eventMask; + + +}; + +class k8Event_L1_DTLB_MISSES_AND_L2_DTLB_HITS : public k8BaseEvent +{ + k8Event_L1_DTLB_MISSES_AND_L2_DTLB_HITS() + { + + event_id = 0x45; + unitEncode = DC; + + eventMask = (EVENT_MASK(NULL_MASK) *)&m_eventMask; + + name = _T("L1 DTLB misses and L2 DTLB hits"); + } + EVENT_MASK(NULL_MASK) * eventMask; + + +}; + +class k8Event_L1_AND_L2_DTLB_MISSES : public k8BaseEvent +{ + k8Event_L1_AND_L2_DTLB_MISSES() + { + + event_id = 0x46; + unitEncode = DC; + + eventMask = (EVENT_MASK(NULL_MASK) *)&m_eventMask; + + name = _T("L1 and L2 DTLB misses") ; + } + EVENT_MASK(NULL_MASK) * eventMask; + +}; + +class k8Event_MISALIGNED_DATA_REFERENCES : public k8BaseEvent +{ + k8Event_MISALIGNED_DATA_REFERENCES() + { + + event_id = 0x47; + unitEncode = DC; + + eventMask = (EVENT_MASK(NULL_MASK) *)&m_eventMask; + + //NULL, _T("MISALIGNED_DATA_REFERENCES"), + name = _T("Misaligned data references"); + } + EVENT_MASK(NULL_MASK) * eventMask; + +}; + + + +class k8Event_ACCESS_CANCEL_LATE : public k8BaseEvent +{ +public: + + k8Event_ACCESS_CANCEL_LATE() + { + eventMask = (EVENT_MASK(NULL_MASK) *)&m_eventMask; + name = _T("Microarchitectural late cancel of an access"); + event_id = 0x48; + unitEncode = DC; + + } + EVENT_MASK(NULL_MASK) * eventMask; + // name = _T("ACCESS_CANCEL_LATE"); + + +}; +class k8Event_ACCESS_CANCEL_EARLY : public k8BaseEvent +{ +public: + + k8Event_ACCESS_CANCEL_EARLY() + { + eventMask = (EVENT_MASK(NULL_MASK) *)&m_eventMask; + name = _T("Microarchitectural early cancel of an access"); + event_id = 0x49; + unitEncode = DC; + + } + EVENT_MASK(NULL_MASK) * eventMask; + // name = _T("ACCESS_CANCEL_EARLY"); + + +}; +typedef union EVENT_MASK( k8_ecc) +{ + struct + { + uint16 ScrubberError : 1; // Scrubber error" }, + uint16 PiggybackScrubberErrors : 1; // Piggyback scrubber errors" } } + }; + uint16 flat; + +}EVENT_MASK( k8_ecc); + + +class k8Event_ECC_BIT_ERR : public k8BaseEvent +{ +public: + + k8Event_ECC_BIT_ERR() + { + eventMask = (EVENT_MASK(k8_ecc) *)&m_eventMask; + name = _T("One bit ECC error recorded found by scrubber"); + event_id = 0x4A; + unitEncode = DC; + + } + EVENT_MASK(k8_ecc) * eventMask; + // name = _T("ECC_BIT_ERR"); + + +}; + +// 4B +typedef union EVENT_MASK( k8_distpatch_prefetch_instructions) +{ + struct + { + uint16 Load : 1; + uint16 Store : 1; + uint16 NTA : 1; + }; + uint16 flat; + + +}EVENT_MASK( k8_distpatch_prefetch_instructions); + +class k8Event_DISPATCHED_PRE_INSTRS : public k8BaseEvent +{ +public: + + k8Event_DISPATCHED_PRE_INSTRS() + { + eventMask = (EVENT_MASK(k8_distpatch_prefetch_instructions) *)&m_eventMask; + name = _T("Dispatched prefetch instructions"); + event_id = 0x4B; + unitEncode = DC; + + } + EVENT_MASK(k8_distpatch_prefetch_instructions) * eventMask; + // name = _T("DISPATCHED_PRE_INSTRS"); + + /* 0x4C: added in Revision C */ + +}; + + + +typedef union EVENT_MASK( k8_lock_accesses) +{ + struct + { + uint16 DcacheAccesses:1; // Number of dcache accesses by lock instructions" }, + uint16 DcacheMisses:1; // Number of dcache misses by lock instructions" } } + }; + uint16 flat; + +}EVENT_MASK( k8_lock_accesses); + + + +class k8Event_LOCK_ACCESSES : public k8BaseEvent +{ +public: + + k8Event_LOCK_ACCESSES() + { + eventMask = (EVENT_MASK(k8_lock_accesses) *)&m_eventMask; + name = _T("DCACHE accesses by locks") ; + event_id = 0x4C; + unitEncode = DC; + + revRequired = 'C'; + } + EVENT_MASK(k8_lock_accesses) * eventMask; + + +}; + + +class k8Event_CYCLES_PROCESSOR_IS_RUNNING : public k8BaseEvent +{ +public: + + k8Event_CYCLES_PROCESSOR_IS_RUNNING() + { + eventMask = (EVENT_MASK(NULL_MASK) *)&m_eventMask; + name = _T("Cycles processor is running (not in HLT or STPCLK)"); + event_id = 0x76; + unitEncode = BU; + + } + EVENT_MASK(NULL_MASK) * eventMask; + // name = _T("CYCLES_PROCESSOR_IS_RUNNING"); /* undocumented *; + + +}; + + +typedef union EVENT_MASK( k8_internal_L2_request) +{ + struct + { + uint16 ICFill:1; // IC fill" }, + uint16 DCFill:1; // DC fill" }, + uint16 TLBReload:1; // TLB reload" }, + uint16 TagSnoopRequest:1; // Tag snoop request" }, + uint16 CancelledRequest:1; // Cancelled request" } } + }; + uint16 flat; + + +}EVENT_MASK( k8_internal_L2_request); + +class k8Event_BU_INT_L2_REQ : public k8BaseEvent +{ +public: + + k8Event_BU_INT_L2_REQ() + { + eventMask = (EVENT_MASK(k8_internal_L2_request) *)&m_eventMask; + name = _T("Internal L2 request"); + unitEncode = BU; + event_id = 0x7D; + } + + EVENT_MASK(k8_internal_L2_request) * eventMask; +} ; + + // name = _T("BU_INT_L2_REQ"); + + + +// 7E +typedef union EVENT_MASK( k8_fill_request_missed_L2) +{ + + struct + { + uint16 ICFill:1; // IC fill" }, + uint16 DCFill:1; // DC fill" }, + uint16 TLBReload:1; // TLB reload" }, + }; + uint16 flat; + +} EVENT_MASK( k8_fill_request_missed_L2); + + +class k8Event_BU_FILL_REQ : public k8BaseEvent +{ +public: + + k8Event_BU_FILL_REQ() + { + eventMask = (EVENT_MASK(k8_fill_request_missed_L2) *)&m_eventMask; + name = _T("Fill request that missed in L2"); + event_id = 0x7E; + unitEncode = BU; + + } + EVENT_MASK(k8_fill_request_missed_L2) * eventMask; + // name = _T("BU_FILL_REQ"); + + + +}; + + + + +// 7F +typedef union EVENT_MASK( k8_fill_into_L2) +{ + + struct + { + uint16 DirtyL2Victim:1; // Dirty L2 victim + uint16 VictimFromL2:1; // Victim from L2 + }; + uint16 flat; + +}EVENT_MASK( k8_fill_into_L2); + +class k8Event_BU_FILL_L2 : public k8BaseEvent +{ +public: + + k8Event_BU_FILL_L2() + { + eventMask = (EVENT_MASK(k8_fill_into_L2) *)&m_eventMask; + name = _T("Fill into L2"); + event_id = 0x7F; + unitEncode = BU; + + } + EVENT_MASK(k8_fill_into_L2) * eventMask; + // name = _T("BU_FILL_L2"); + + +}; + +class k8Event_INSTRUCTION_CACHE_FETCHES : public k8BaseEvent +{ +public: + k8Event_INSTRUCTION_CACHE_FETCHES() + { + event_id = 0x80; + unitEncode = IC; + + eventMask = (EVENT_MASK(NULL_MASK) *)&m_eventMask; + + name = _T("Instruction cache fetches"); + } + EVENT_MASK(NULL_MASK) * eventMask; + +}; + + +class k8Event_INSTRUCTION_CACHE_MISSES : public k8BaseEvent +{ +public: + k8Event_INSTRUCTION_CACHE_MISSES() + { + event_id = 0x81; + unitEncode = IC; + + eventMask = (EVENT_MASK(NULL_MASK) *)&m_eventMask; + + //0xF, NULL, _T("INSTRUCTION_CACHE_MISSES"), + name = _T("Instruction cache misses"); + } + EVENT_MASK(NULL_MASK) * eventMask; + +}; + + +class k8Event_IC_REFILL_FROM_L2 : public k8BaseEvent +{ +public: + + k8Event_IC_REFILL_FROM_L2() + { + eventMask = (EVENT_MASK(k8_cache) *)&m_eventMask; + name = _T("Refill from L2"); + event_id = 0x82; + unitEncode = IC; + + } + EVENT_MASK(k8_cache) * eventMask; + // name = _T("IC_REFILL_FROM_L2"); + + + +}; +class k8Event_IC_REFILL_FROM_SYS : public k8BaseEvent +{ +public: + + k8Event_IC_REFILL_FROM_SYS() + { + eventMask = (EVENT_MASK(k8_cache) *)&m_eventMask; + name = _T("Refill from system"); + event_id = 0x83; + unitEncode = IC; + + } + EVENT_MASK(k8_cache) * eventMask; + // name = _T("IC_REFILL_FROM_SYS"); + + + +}; +class k8Event_L1_ITLB_MISSES_AND_L2_ITLB_HITS : public k8BaseEvent +{ +public: + k8Event_L1_ITLB_MISSES_AND_L2_ITLB_HITS() + { + + event_id = 0x84; + unitEncode = IC; + + name = _T("L1 ITLB misses (and L2 ITLB hits)"); + eventMask = (EVENT_MASK(NULL_MASK) *)&m_eventMask; + + + } + EVENT_MASK(NULL_MASK) * eventMask; + + +}; + +class k8Event_L1_AND_L2_ITLB_MISSES : public k8BaseEvent +{ +public: + k8Event_L1_AND_L2_ITLB_MISSES() + { + event_id = 0x85; + unitEncode = IC; + + name = _T("(L1 and) L2 ITLB misses"); + eventMask = (EVENT_MASK(NULL_MASK) *)&m_eventMask; + + } + EVENT_MASK(NULL_MASK) * eventMask; + +}; + + + + +class k8Event_IC_RESYNC_BY_SNOOP : public k8BaseEvent +{ +public: + + k8Event_IC_RESYNC_BY_SNOOP() + { + eventMask = (EVENT_MASK(NULL_MASK) *)&m_eventMask; + event_id = 0x86; + unitEncode = IC; + + name = _T("Microarchitectural resync caused by snoop"); + } + EVENT_MASK(NULL_MASK) * eventMask; + // name = _T("IC_RESYNC_BY_SNOOP"); + /* similar to 0x22; but IC unit instead of LS unit */ + + + +}; +class k8Event_IC_FETCH_STALL : public k8BaseEvent +{ +public: + + k8Event_IC_FETCH_STALL() + { + eventMask = (EVENT_MASK(NULL_MASK) *)&m_eventMask; + name = _T("Instruction fetch stall"); + event_id = 0x87; + unitEncode = IC; + + } + EVENT_MASK(NULL_MASK) * eventMask; + // name = _T("IC_FETCH_STALL"); + + + +}; +class k8Event_IC_STACK_HIT : public k8BaseEvent +{ +public: + + k8Event_IC_STACK_HIT() + { + eventMask = (EVENT_MASK(NULL_MASK) *)&m_eventMask; + name = _T("Return stack hit"); + event_id = 0x88; + unitEncode = IC; + + } + EVENT_MASK(NULL_MASK) * eventMask; + // name = _T("IC_STACK_HIT"); + + + +}; +class k8Event_IC_STACK_OVERFLOW : public k8BaseEvent +{ +public: + + k8Event_IC_STACK_OVERFLOW() + { + eventMask = (EVENT_MASK(NULL_MASK) *)&m_eventMask; + name = _T("Return stack overflow"); + event_id = 0x89; + unitEncode = IC; + + } + EVENT_MASK(NULL_MASK) * eventMask; + // name = _T("IC_STACK_OVERFLOW"); + + + + +}; + + /* 0xC0-0xC7: from K7 official event set */ +class k8Event_RETIRED_INSTRUCTIONS : public k8BaseEvent +{ +public: + k8Event_RETIRED_INSTRUCTIONS() + { + event_id = 0xC0; + unitEncode = FR; + + eventMask = (EVENT_MASK(NULL_MASK) *)&m_eventMask; + + //0xF, NULL, _T("RETIRED_INSTRUCTIONS"), + name = _T("Retired instructions (includes exceptions, interrupts, resyncs)"); + } + EVENT_MASK(NULL_MASK) * eventMask; +}; + +class k8Event_RETIRED_OPS : public k8BaseEvent +{ +public: + k8Event_RETIRED_OPS() + { + event_id = 0xC1; + unitEncode = FR; + + eventMask = (EVENT_MASK(NULL_MASK) *)&m_eventMask; + //0xF, NULL, _T("RETIRED_OPS"), + name = _T("Retired Ops") ; + } + EVENT_MASK(NULL_MASK) * eventMask; +}; +class k8Event_RETIRED_BRANCHES : public k8BaseEvent +{ +public: + k8Event_RETIRED_BRANCHES() + { + event_id = 0xC2; + unitEncode = FR; + + eventMask = (EVENT_MASK(NULL_MASK) *)&m_eventMask; + //0xF, NULL, _T("RETIRED_BRANCHES"), + name = _T("Retired branches (conditional, unconditional, exceptions, interrupts)") ; + } + EVENT_MASK(NULL_MASK) * eventMask; +}; +class k8Event_RETIRED_BRANCHES_MISPREDICTED : public k8BaseEvent +{ +public: + k8Event_RETIRED_BRANCHES_MISPREDICTED() + { + event_id = 0xC3; + unitEncode = FR; + + eventMask = (EVENT_MASK(NULL_MASK) *)&m_eventMask; + //0xF, NULL, _T("RETIRED_BRANCHES_MISPREDICTED"), + name = _T("Retired branches mispredicted") ; + } + EVENT_MASK(NULL_MASK) * eventMask; +}; +class k8Event_RETIRED_TAKEN_BRANCHES : public k8BaseEvent +{ +public: + k8Event_RETIRED_TAKEN_BRANCHES() + { + event_id = 0xC4; + unitEncode = FR; + + eventMask = (EVENT_MASK(NULL_MASK) *)&m_eventMask; + //0xF, NULL, _T("RETIRED_TAKEN_BRANCHES"), + name = _T("Retired taken branches") ; + } + EVENT_MASK(NULL_MASK) * eventMask; +}; +class k8Event_RETIRED_TAKEN_BRANCHES_MISPREDICTED : public k8BaseEvent +{ +public: + k8Event_RETIRED_TAKEN_BRANCHES_MISPREDICTED() + { + event_id = 0xC5; + unitEncode = FR; + + eventMask = (EVENT_MASK(NULL_MASK) *)&m_eventMask; + //0xF, NULL, _T("RETIRED_TAKEN_BRANCHES_MISPREDICTED"), + name = _T("Retired taken branches mispredicted") ; + } + EVENT_MASK(NULL_MASK) * eventMask; +}; +class k8Event_RETIRED_FAR_CONTROL_TRANSFERS : public k8BaseEvent +{ +public: + k8Event_RETIRED_FAR_CONTROL_TRANSFERS() + { + event_id = 0xC6; + unitEncode = FR; + + eventMask = (EVENT_MASK(NULL_MASK) *)&m_eventMask; + //0xF, NULL, _T("RETIRED_FAR_CONTROL_TRANSFERS"), + name = _T("Retired far control transfers") ; + } + EVENT_MASK(NULL_MASK) * eventMask; +}; +class k8Event_RETIRED_RESYNC_BRANCHES : public k8BaseEvent +{ +public: + k8Event_RETIRED_RESYNC_BRANCHES() + { + event_id = 0xC7; + unitEncode = FR; + + eventMask = (EVENT_MASK(NULL_MASK) *)&m_eventMask; + //0xF, NULL, _T("RETIRED_RESYNC_BRANCHES"), + name = _T("Retired resync branches (only non-control transfer branches counted)") ; + } + EVENT_MASK(NULL_MASK) * eventMask; +}; + +class k8Event_RETIRED_NEAR_RETURNS : public k8BaseEvent +{ +public: + + k8Event_RETIRED_NEAR_RETURNS() + { + eventMask = (EVENT_MASK(NULL_MASK) *)&m_eventMask; + name = _T("Retired near returns"); + event_id = 0xC8; + unitEncode = FR; + + } + EVENT_MASK(NULL_MASK) * eventMask; + + + +}; +class k8Event_RETIRED_RETURNS_MISPREDICT : public k8BaseEvent +{ +public: + + k8Event_RETIRED_RETURNS_MISPREDICT() + { + eventMask = (EVENT_MASK(NULL_MASK) *)&m_eventMask; + name = _T("Retired near returns mispredicted"); + event_id = 0xC9; + unitEncode = FR; + + } + EVENT_MASK(NULL_MASK) * eventMask; + // name = _T("RETIRED_RETURNS_MISPREDICT"); + + +}; +class k8Event_RETIRED_BRANCH_MISCOMPARE : public k8BaseEvent +{ +public: + + k8Event_RETIRED_BRANCH_MISCOMPARE() + { + eventMask = (EVENT_MASK(NULL_MASK) *)&m_eventMask; + name = _T("Retired taken branches mispredicted due to address miscompare"); + event_id = 0xCA; + unitEncode = FR; + + } + EVENT_MASK(NULL_MASK) * eventMask; + // name = _T("RETIRED_BRANCH_MISCOMPARE"); + + +}; + + + /* Revision B and later */ + +typedef union EVENT_MASK( k8_retired_fpu_instr) +{ + struct + { + uint16 DirtyL2Victim:1; // x87 instructions + uint16 CombinedMMX_3DNow:1; // Combined MMX & 3DNow! instructions" }, + uint16 CombinedPackedSSE_SSE2:1; // Combined packed SSE and SSE2 instructions" }, + uint16 CombinedScalarSSE_SSE2:1; // Combined scalar SSE and SSE2 instructions" } } + }; + uint16 flat; + + +}EVENT_MASK( k8_retired_fpu_instr); + + +class k8Event_RETIRED_FPU_INSTRS : public k8BaseEvent +{ +public: + + k8Event_RETIRED_FPU_INSTRS() + { + eventMask = (EVENT_MASK(k8_retired_fpu_instr) *)&m_eventMask; + event_id = 0xCB; + unitEncode = FR; + + name = _T("Retired FPU instructions"); + revRequired = 'B'; + } + EVENT_MASK(k8_retired_fpu_instr) * eventMask; + /* Revision B and later */ + + + +}; + +// CC +typedef union EVENT_MASK( k8_retired_fastpath_double_op_instr ) +{ + + struct + { + uint16 LowOpPosition0:1; // With low op in position 0" }, + uint16 LowOpPosition1:1; // With low op in position 1" }, + uint16 LowOpPosition2:1; // With low op in position 2" } } + }; + uint16 flat; + + +}EVENT_MASK( k8_retired_fastpath_double_op_instr); + +class k8Event_RETIRED_FASTPATH_INSTRS : public k8BaseEvent +{ +public: + + k8Event_RETIRED_FASTPATH_INSTRS() + { + eventMask = (EVENT_MASK(k8_retired_fastpath_double_op_instr) *)&m_eventMask; + event_id = 0xCC; + unitEncode = FR; + + name = _T("Retired fastpath double op instructions"); + revRequired = 'B'; + + } + EVENT_MASK(k8_retired_fastpath_double_op_instr) * eventMask; + + +}; + +class k8Event_INTERRUPTS_MASKED_CYCLES : public k8BaseEvent +{ +public: + k8Event_INTERRUPTS_MASKED_CYCLES() + { + event_id = 0xCD; + unitEncode = FR; + + //0xF, NULL, _T("INTERRUPTS_MASKED_CYCLES"), + name = _T("Interrupts masked cycles (IF=0)") ; + eventMask = (EVENT_MASK(NULL_MASK) *)&m_eventMask; + + } + EVENT_MASK(NULL_MASK) * eventMask; + +}; +class k8Event_INTERRUPTS_MASKED_WHILE_PENDING_CYCLES : public k8BaseEvent +{ +public: + k8Event_INTERRUPTS_MASKED_WHILE_PENDING_CYCLES() + { + event_id = 0xCE; + unitEncode = FR; + + //0xF, NULL, _T("INTERRUPTS_MASKED_WHILE_PENDING_CYCLES"), + name = _T("Interrupts masked while pending cycles (INTR while IF=0)") ; + eventMask = (EVENT_MASK(NULL_MASK) *)&m_eventMask; + } + EVENT_MASK(NULL_MASK) * eventMask; + +}; +class k8Event_NUMBER_OF_TAKEN_HARDWARE_INTERRUPTS : public k8BaseEvent +{ +public: + k8Event_NUMBER_OF_TAKEN_HARDWARE_INTERRUPTS() + { + event_id = 0xCF; + unitEncode = FR; + + //0xF, NULL, _T("NUMBER_OF_TAKEN_HARDWARE_INTERRUPTS"), + name = _T("Number of taken hardware interrupts") ; + eventMask = (EVENT_MASK(NULL_MASK) *)&m_eventMask; + } + EVENT_MASK(NULL_MASK) * eventMask; + +}; + + +class k8Event_DECODER_EMPTY : public k8BaseEvent +{ +public: + + k8Event_DECODER_EMPTY() + { + eventMask = (EVENT_MASK(NULL_MASK) *)&m_eventMask; + name = _T("Nothing to dispatch (decoder empty)"); + event_id = 0xD0; + unitEncode = FR; + + } + EVENT_MASK(NULL_MASK) * eventMask; + // name = _T("DECODER_EMPTY"); + + +}; +class k8Event_DISPATCH_STALLS : public k8BaseEvent +{ +public: + + k8Event_DISPATCH_STALLS() + { + eventMask = (EVENT_MASK(NULL_MASK) *)&m_eventMask; + name = _T("Dispatch stalls (events 0xD2-0xDA combined)"); + event_id = 0xD1; + unitEncode = FR; + + } + EVENT_MASK(NULL_MASK) * eventMask; + // name = _T("DISPATCH_STALLS"); + + + +}; +class k8Event_DISPATCH_STALL_FROM_BRANCH_ABORT : public k8BaseEvent +{ +public: + + k8Event_DISPATCH_STALL_FROM_BRANCH_ABORT() + { + eventMask = (EVENT_MASK(NULL_MASK) *)&m_eventMask; + name = _T("Dispatch stall from branch abort to retire"); + event_id = 0xD2; + unitEncode = FR; + + } + EVENT_MASK(NULL_MASK) * eventMask; + // name = _T("DISPATCH_STALL_FROM_BRANCH_ABORT"); + + + +}; +class k8Event_DISPATCH_STALL_SERIALIZATION : public k8BaseEvent +{ +public: + + k8Event_DISPATCH_STALL_SERIALIZATION() + { + eventMask = (EVENT_MASK(NULL_MASK) *)&m_eventMask; + name = _T("Dispatch stall for serialization"); + event_id = 0xD3; + unitEncode = FR; + + } + EVENT_MASK(NULL_MASK) * eventMask; + // name = _T("DISPATCH_STALL_SERIALIZATION"); + + +}; +class k8Event_DISPATCH_STALL_SEG_LOAD : public k8BaseEvent +{ +public: + + k8Event_DISPATCH_STALL_SEG_LOAD() + { + eventMask = (EVENT_MASK(NULL_MASK) *)&m_eventMask; + name = _T("Dispatch stall for segment load"); + event_id = 0xD4; + unitEncode = FR; + + } + EVENT_MASK(NULL_MASK) * eventMask; + // name = _T("DISPATCH_STALL_SEG_LOAD"); + + + +}; +class k8Event_DISPATCH_STALL_REORDER_BUFFER : public k8BaseEvent +{ +public: + + k8Event_DISPATCH_STALL_REORDER_BUFFER() + { + eventMask = (EVENT_MASK(NULL_MASK) *)&m_eventMask; + name = _T("Dispatch stall when reorder buffer is full"); + event_id = 0xD5; + unitEncode = FR; + + } + EVENT_MASK(NULL_MASK) * eventMask; + // name = _T("DISPATCH_STALL_REORDER_BUFFER"); + + +}; +class k8Event_DISPATCH_STALL_RESERVE_STATIONS : public k8BaseEvent +{ +public: + + k8Event_DISPATCH_STALL_RESERVE_STATIONS() + { + eventMask = (EVENT_MASK(NULL_MASK) *)&m_eventMask; + name = _T("Dispatch stall when reservation stations are full"); + event_id = 0xD6; + unitEncode = FR; + + } + EVENT_MASK(NULL_MASK) * eventMask; + // name = _T("DISPATCH_STALL_RESERVE_STATIONS"); + + +}; +class k8Event_DISPATCH_STALL_FPU : public k8BaseEvent +{ +public: + + k8Event_DISPATCH_STALL_FPU() + { + eventMask = (EVENT_MASK(NULL_MASK) *)&m_eventMask; + name = _T("Dispatch stall when FPU is full"); + event_id = 0xD7; + unitEncode = FR; + + } + EVENT_MASK(NULL_MASK) * eventMask; + // name = _T("DISPATCH_STALL_FPU"); + + +}; +class k8Event_DISPATCH_STALL_LS : public k8BaseEvent +{ +public: + + k8Event_DISPATCH_STALL_LS() + { + eventMask = (EVENT_MASK(NULL_MASK) *)&m_eventMask; + name = _T("Dispatch stall when LS is full"); + event_id = 0xD8; + unitEncode = FR; + + } + EVENT_MASK(NULL_MASK) * eventMask; + // name = _T("DISPATCH_STALL_LS"); + + +}; +class k8Event_DISPATCH_STALL_QUIET_WAIT : public k8BaseEvent +{ +public: + + k8Event_DISPATCH_STALL_QUIET_WAIT() + { + eventMask = (EVENT_MASK(NULL_MASK) *)&m_eventMask; + name = _T("Dispatch stall when waiting for all to be quiet"); + event_id = 0xD9; + unitEncode = FR; + + } + EVENT_MASK(NULL_MASK) * eventMask; + // name = _T("DISPATCH_STALL_QUIET_WAIT"); + + + +}; +class k8Event_DISPATCH_STALL_PENDING : public k8BaseEvent +{ +public: + + k8Event_DISPATCH_STALL_PENDING() + { + eventMask = (EVENT_MASK(NULL_MASK) *)&m_eventMask; + name = _T("Dispatch stall when far control transfer or resync branch is pending"); + event_id = 0xDA; + unitEncode = FR; + + } + EVENT_MASK(NULL_MASK) * eventMask; + // name = _T("DISPATCH_STALL_PENDING"); + + + +}; + + +typedef union EVENT_MASK( k8_fpu_exceptions) +{ + + + + struct + { + uint16 x87ReclassMicrofaults:1; // x87 reclass microfaults" }, + uint16 SSERetypeMicrofaults:1; // SSE retype microfaults" }, + uint16 SSEReclassMicrofaults:1; // SSE reclass microfaults" }, + uint16 SSE_x87Microtraps:1; // SSE and x87 microtraps" } } + }; + uint16 flat; + + + +}EVENT_MASK( k8_fpu_exceptions); + +class k8Event_FPU_EXCEPTIONS : public k8BaseEvent +{ +public: + + k8Event_FPU_EXCEPTIONS() + { + eventMask = (EVENT_MASK(k8_fpu_exceptions) *)&m_eventMask; + event_id = 0xDB; + unitEncode = FR; + + name = _T("FPU exceptions"); + revRequired = 'B'; + + } + EVENT_MASK(k8_fpu_exceptions) * eventMask; + // name = _T("FPU_EXCEPTIONS"); + /* Revision B and later */ + + + +}; +class k8Event_DR0_BREAKPOINTS : public k8BaseEvent +{ +public: + + k8Event_DR0_BREAKPOINTS() + { + eventMask = (EVENT_MASK(NULL_MASK) *)&m_eventMask; + name = _T("Number of breakpoints for DR0"); + event_id = 0xDC; + unitEncode = FR; + + } + EVENT_MASK(NULL_MASK) * eventMask; + // name = _T("DR0_BREAKPOINTS"); + + + +}; +class k8Event_DR1_BREAKPOINTS : public k8BaseEvent +{ +public: + + k8Event_DR1_BREAKPOINTS() + { + eventMask = (EVENT_MASK(NULL_MASK) *)&m_eventMask; + name = _T("Number of breakpoints for DR1"); + event_id = 0xDD; + unitEncode = FR; + + } + EVENT_MASK(NULL_MASK) * eventMask; + // name = _T("DR1_BREAKPOINTS"); + + + +}; +class k8Event_DR2_BREAKPOINTS : public k8BaseEvent +{ +public: + + k8Event_DR2_BREAKPOINTS() + { + eventMask = (EVENT_MASK(NULL_MASK) *)&m_eventMask; + name = _T("Number of breakpoints for DR2"); + event_id = 0xDE; + unitEncode = FR; + + } + EVENT_MASK(NULL_MASK) * eventMask; + // name = _T("DR2_BREAKPOINTS"); + + +}; +class k8Event_DR3_BREAKPOINTS : public k8BaseEvent +{ +public: + + k8Event_DR3_BREAKPOINTS() + { + eventMask = (EVENT_MASK(NULL_MASK) *)&m_eventMask; + name = _T("Number of breakpoints for DR3"); + event_id = 0xDF; + unitEncode = FR; + + } + EVENT_MASK(NULL_MASK) * eventMask; + // name = _T("DR3_BREAKPOINTS"); + + +}; + + + +// E0 +typedef union EVENT_MASK( k8_page_access_event) +{ + struct + { + uint16 PageHit:1; // Page hit" }, + uint16 PageMiss:1; // Page miss" }, + uint16 PageConflict:1; // Page conflict" } } + }; + uint16 flat; + +}EVENT_MASK( k8_page_access_event); + +class k8Event_MEM_PAGE_ACCESS : public k8BaseEvent +{ +public: + + k8Event_MEM_PAGE_ACCESS() + { + eventMask = (EVENT_MASK(k8_page_access_event) *)&m_eventMask; + name = _T("Memory controller page access"); + event_id = 0xE0; + unitEncode = NB; + + } + EVENT_MASK(k8_page_access_event) * eventMask; + // name = _T("MEM_PAGE_ACCESS"); + + +}; +class k8Event_MEM_PAGE_TBL_OVERFLOW : public k8BaseEvent +{ +public: + + k8Event_MEM_PAGE_TBL_OVERFLOW() + { + eventMask = (EVENT_MASK(NULL_MASK) *)&m_eventMask; + name = _T("Memory controller page table overflow"); + event_id = 0xE1; + unitEncode = NB; + + } + EVENT_MASK(NULL_MASK) * eventMask; + // name = _T("MEM_PAGE_TBL_OVERFLOW"); + + +}; +class k8Event_DRAM_SLOTS_MISSED : public k8BaseEvent +{ +public: + + k8Event_DRAM_SLOTS_MISSED() + { + eventMask = (EVENT_MASK(NULL_MASK) *)&m_eventMask; + name = _T("Memory controller DRAM command slots missed (in MemClks)"); + event_id = 0xE2; + unitEncode = NB; + + } + EVENT_MASK(NULL_MASK) * eventMask; + // name = _T("DRAM_SLOTS_MISSED"); + + +}; + + +// e3 +typedef union EVENT_MASK( k8_turnaround) +{ + + struct + { + uint16 DIMMTurnaround:1; //DIMM turnaround" }, + uint16 ReadToWriteTurnaround:1; //Read to write turnaround" }, + uint16 WriteToReadTurnaround:1; //Write to read turnaround" } } + }; + uint16 flat; + + +}EVENT_MASK( k8_turnaround); + +class k8Event_MEM_TURNAROUND : public k8BaseEvent +{ +public: + + k8Event_MEM_TURNAROUND() + { + eventMask = (EVENT_MASK(k8_turnaround) *)&m_eventMask; + name = _T("Memory controller turnaround"); + event_id = 0xE3; + unitEncode = NB; + + } + EVENT_MASK(k8_turnaround) * eventMask; + // name = _T("MEM_TURNAROUND"); + + +}; + + + + +// E4 +typedef union EVENT_MASK( k8_bypass_counter_saturation) +{ + struct + { + uint16 MEM_HighPriorityBypass:1; // Memory controller high priority bypass" }, + uint16 MEM_LowPriorityBypass:1; // Memory controller low priority bypass" }, + uint16 DRAM_InterfaceBypass:1; // DRAM controller interface bypass" }, + uint16 DRAM_QueueBypass:1; // DRAM controller queue bypass" } } + }; + uint16 flat; + +}EVENT_MASK( k8_bypass_counter_saturation); + +class k8Event_MEM_BYPASS_SAT : public k8BaseEvent +{ +public: + + k8Event_MEM_BYPASS_SAT() + { + eventMask = (EVENT_MASK(k8_bypass_counter_saturation) *)&m_eventMask; + name = _T("Memory controller bypass counter saturation"); + event_id = 0xE4; + unitEncode = NB; + + } + EVENT_MASK(k8_bypass_counter_saturation) * eventMask; + // name = _T("MEM_BYPASS_SAT"); + + +}; + + + +//EB +typedef union EVENT_MASK( k8_sized_commands) +{ + + struct + { + uint16 NonPostWrSzByte:1; // NonPostWrSzByte" }, + uint16 NonPostWrSzDword:1; // NonPostWrSzDword" }, + uint16 PostWrSzByte:1; // PostWrSzByte" }, + uint16 PostWrSzDword:1; // PostWrSzDword" }, + uint16 RdSzByte:1; // RdSzByte" }, + uint16 RdSzDword:1; // RdSzDword" }, + uint16 RdModWr:1; // RdModWr" } } + }; + uint16 flat; + + +}EVENT_MASK( k8_sized_commands); + + +class k8Event_SIZED_COMMANDS : public k8BaseEvent +{ +public: + + k8Event_SIZED_COMMANDS() + { + eventMask = (EVENT_MASK(k8_sized_commands) *)&m_eventMask; + name = _T("Sized commands"); + event_id = 0xEB; + unitEncode = NB; + + } + EVENT_MASK(k8_sized_commands) * eventMask; + // name = _T("SIZED_COMMANDS"); + + +}; + +typedef union EVENT_MASK( k8_probe_result) +{ + struct + { + uint16 ProbeMiss:1; // Probe miss" }, + uint16 ProbeHit:1; // Probe hit" }, + uint16 ProbeHitDirtyWithoutMemoryCancel:1; // Probe hit dirty without memory cancel" }, + uint16 ProbeHitDirtyWithMemoryCancel:1; // Probe hit dirty with memory cancel" } } + uint16 UpstreamDisplayRefreshReads:1; // Rev D and later + uint16 UpstreamNonDisplayRefreshReads:1; // Rev D and later + uint16 UpstreamWrites:1; // Rev D and later + }; + uint16 flat; + + +}EVENT_MASK( k8_probe_result); + + +class k8Event_PROBE_RESULT : public k8BaseEvent +{ +public: + + k8Event_PROBE_RESULT() + { + eventMask = (EVENT_MASK(k8_probe_result) *)&m_eventMask; + name = _T("Probe result"); + event_id = 0xEC; + unitEncode = NB; + + } + EVENT_MASK(k8_probe_result) * eventMask; + // name = _T("PROBE_RESULT"); + + +}; + +typedef union EVENT_MASK( k8_ht) +{ + + struct + { + uint16 CommandSent:1; //Command sent" }, + uint16 DataSent:1; //Data sent" }, + uint16 BufferReleaseSent:1; //Buffer release sent" + uint16 NopSent:1; //Nop sent" } } + }; + uint16 flat; + + +}EVENT_MASK( k8_ht); + + +class k8Event_HYPERTRANSPORT_BUS0_WIDTH : public k8BaseEvent +{ +public: + + k8Event_HYPERTRANSPORT_BUS0_WIDTH() + { + eventMask = (EVENT_MASK(k8_ht) *)&m_eventMask; + name = _T("Hypertransport (tm) bus 0 bandwidth"); + event_id = 0xF6; + unitEncode = NB; + + } + EVENT_MASK(k8_ht) * eventMask; + // name = _T("HYPERTRANSPORT_BUS0_WIDTH"); + + +}; +class k8Event_HYPERTRANSPORT_BUS1_WIDTH : public k8BaseEvent +{ +public: + + k8Event_HYPERTRANSPORT_BUS1_WIDTH() + { + eventMask = (EVENT_MASK(k8_ht) *)&m_eventMask; + name = _T("Hypertransport (tm) bus 1 bandwidth"); + event_id = 0xF7; + unitEncode = NB; + + } + EVENT_MASK(k8_ht) * eventMask; + // name = _T("HYPERTRANSPORT_BUS1_WIDTH"); + + +}; +class k8Event_HYPERTRANSPORT_BUS2_WIDTH : public k8BaseEvent +{ +public: + + k8Event_HYPERTRANSPORT_BUS2_WIDTH() + { + eventMask = (EVENT_MASK(k8_ht) *)&m_eventMask; + name = _T("Hypertransport (tm) bus 2 bandwidth"); + event_id = 0xF8; + unitEncode = NB; + + } + EVENT_MASK(k8_ht) * eventMask; + // name = _T("HYPERTRANSPORT_BUS2_WIDTH"); + +}; + +// +//typedef union EVENT_MASK( perfctr_event_set k8_common_event_set) +//{ +// +// .cpu_type = PERFCTR_X86_AMD_K8, +// .event_prefix = _T("K8_"), +// .include = &k7_official_event_set, +// .nevents = ARRAY_SIZE(k8_common_events), +// .events = k8_common_events, +//}EVENT_MASK( perfctr_event_set k8_common_event_set); +// +//typedef union EVENT_MASK( perfctr_event k8_events[]) +//{ +// +// { 0x24, 0xF, UM(NULL), _T("LOCKED_OP"), /* unit mask changed in Rev. C */ +// _T("Locked operation") }, +//}EVENT_MASK( perfctr_event k8_events[]); + + + + +//const struct perfctr_event_set perfctr_k8_event_set) +//{ +// +// .cpu_type = PERFCTR_X86_AMD_K8, +// .event_prefix = _T("K8_"), +// .include = &k8_common_event_set, +// .nevents = ARRAY_SIZE(k8_events), +// .events = k8_events, +//}; +// +/* + * K8 Revision C. Starts at CPUID 0xF58 for Opteron/Athlon64FX and + * CPUID 0xF48 for Athlon64. (CPUID 0xF51 is Opteron Revision B3.) + */ + + + + + + + + +// +//typedef union EVENT_MASK( k8_lock_accesses) +//{ +// struct +// { +// uint16 DcacheAccesses:1; // Number of dcache accesses by lock instructions" }, +// uint16 DcacheMisses:1; // Number of dcache misses by lock instructions" } } +// }; +// uint16 flat; +// +//}EVENT_MASK( k8_lock_accesses); +// + +#endif // K8PERFORMANCECOUNTERS_H diff --git a/public/tier0/P4PerformanceCounters.h b/public/tier0/P4PerformanceCounters.h new file mode 100644 index 0000000..b52b691 --- /dev/null +++ b/public/tier0/P4PerformanceCounters.h @@ -0,0 +1,322 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +// +//=============================================================================// +#ifndef P4PERFORMANCECOUNTERS_H +#define P4PERFORMANCECOUNTERS_H + +#pragma once +// Pentium 4 support + +/* + http://developer.intel.com/design/Pentium4/documentation.htm + + IA-32 Intel Architecture Software Developer's Manual Volume 1: Basic Architecture + + IA-32 Intel Architecture Software Developer's Manual Volume 2A: Instruction Set Reference, A-M + + IA-32 Intel Architecture Software Developer's Manual Volume 2B: Instruction Set Reference, N-Z + + IA-32 Intel Architecture Software Developer's Manual Volume 3: System Programming Guide + + + From Mikael Pettersson's perfctr: + + http://user.it.uu.se/~mikpe/linux/perfctr/ + + * Known quirks: + - OVF_PMI+FORCE_OVF counters must have an ireset value of -1. + This allows the regular overflow check to also handle FORCE_OVF + counters. Not having this restriction would lead to MAJOR + complications in the driver's "detect overflow counters" code. + There is no loss of functionality since the ireset value doesn't + affect the counter's PMI rate for FORCE_OVF counters. + + - In experiments with FORCE_OVF counters, and regular OVF_PMI + counters with small ireset values between -8 and -1, it appears + that the faulting instruction is subjected to a new PMI before + it can complete, ad infinitum. This occurs even though the driver + clears the CCCR (and in testing also the ESCR) and invokes a + user-space signal handler before restoring the CCCR and resuming + the instruction. +*/ + +#define NCOUNTERS 18 + +// The 18 counters +enum Counters +{ + MSR_BPU_COUNTER0, + MSR_BPU_COUNTER1, + MSR_BPU_COUNTER2, + MSR_BPU_COUNTER3, + MSR_MS_COUNTER0, + MSR_MS_COUNTER1, + MSR_MS_COUNTER2, + MSR_MS_COUNTER3, + MSR_FLAME_COUNTER0, + MSR_FLAME_COUNTER1, + MSR_FLAME_COUNTER2, + MSR_FLAME_COUNTER3, + MSR_IQ_COUNTER0, + MSR_IQ_COUNTER1, + MSR_IQ_COUNTER2, + MSR_IQ_COUNTER3, + MSR_IQ_COUNTER4, + MSR_IQ_COUNTER5 +}; + +// register base for counters +#define MSR_COUNTER_BASE 0x300 + +// register base for CCCR register +#define MSR_CCCR_BASE 0x360 + +#pragma pack(push, 1) +// access to these bits is through the methods +typedef union ESCR +{ + struct + { + uint64 Reserved0_1 : 2; // + uint64 USR : 1; // + uint64 OS : 1; // + uint64 TagEnable : 1; // + uint64 TagValue : 4; // + uint64 EventMask : 16; // from event select + uint64 ESCREventSelect : 6; // 31:25 class of event + uint64 Reserved31 : 1; // + + uint64 Reserved32_63 : 32; // + }; + uint64 flat; + +} ESCR; + +typedef union CCCR +{ + struct + { + uint64 Reserved0_11 : 12;// 0 -11 + uint64 Enable : 1; // 12 + uint64 CCCRSelect : 3; // 13-15 + uint64 Reserved16_17 : 2; // 16 17 + + uint64 Compare : 1; // 18 + uint64 Complement : 1; // 19 + uint64 Threshold : 4; // 20-23 + uint64 Edge : 1; // 24 + uint64 FORCE_OVF : 1; // 25 + uint64 OVF_PMI : 1; // 26 + uint64 Reserved27_29 : 3; // 27-29 + uint64 Cascade : 1; // 30 + uint64 OVF : 1; // 31 + + uint64 Reserved32_63 : 32; // + }; + uint64 flat; + +} CCCR; + +#pragma pack(pop) + +extern const unsigned short cccr_escr_map[NCOUNTERS][8]; + +enum P4TagState +{ + TagDisable, // + TagEnable, // +}; + +enum P4ForceOverflow +{ + ForceOverflowDisable, + ForceOverflowEnable, +}; + +enum P4OverflowInterrupt +{ + OverflowInterruptDisable, + OverflowInterruptEnable, +}; + +// Turn off the no return value warning in ReadCounter. +#pragma warning( disable : 4035 ) +class P4BaseEvent +{ + int m_counter; + +protected: + + void SetCounter(int counter) + { + m_counter = counter; + cccrPort = MSR_CCCR_BASE + m_counter; + counterPort = MSR_COUNTER_BASE + m_counter; + escrPort = cccr_escr_map[m_counter][cccr.CCCRSelect]; + } + +public: + + unsigned short m_eventMask; + const tchar *description; + PME *pme; + ESCR escr; + CCCR cccr; + int counterPort; + int cccrPort; + int escrPort; + + P4BaseEvent() + { + pme = PME::Instance(); + m_eventMask = 0; + description = _T(""); + escr.flat = 0; + cccr.flat = 0; + cccr.Reserved16_17 = 3; // must be set + escrPort = 0; + m_counter = -1; + } + + void StartCounter() + { + cccr.Enable = 1; + pme->WriteMSR( cccrPort, cccr.flat ); + } + + void StopCounter() + { + cccr.Enable = 0; + pme->WriteMSR( cccrPort, cccr.flat ); + } + + void ClearCounter() + { + pme->WriteMSR( counterPort, 0ui64 ); // clear + } + + void WriteCounter( int64 value ) + { + pme->WriteMSR( counterPort, value ); // clear + } + + int64 ReadCounter() + { +#if PME_DEBUG + if ( escr.USR == 0 && escr.OS == 0 ) + return -1; // no area to collect, use SetCaptureMode + + if ( escr.EventMask == 0 ) + return -2; // no event mask set + + if ( m_counter == -1 ) + return -3; // counter not legal +#endif + + // ReadMSR should work here too, but RDPMC should be faster + int64 value = 0; + pme->ReadMSR( counterPort, &value ); + return value; +#if 0 + // we need to copy this into a temp for some reason + int temp = m_counter; + _asm + { + mov ecx, temp + RDPMC + } +#endif + } + + void SetCaptureMode( PrivilegeCapture priv ) + { + switch ( priv ) + { + case OS_Only: + { + escr.USR = 0; + escr.OS = 1; + break; + } + case USR_Only: + { + escr.USR = 1; + escr.OS = 0; + break; + } + case OS_and_USR: + { + escr.USR = 1; + escr.OS = 1; + break; + } + } + + escr.EventMask = m_eventMask; + pme->WriteMSR( escrPort, escr.flat ); + } + + void SetTagging( P4TagState tagEnable, uint8 tagValue ) + { + escr.TagEnable = tagEnable; + escr.TagValue = tagValue; + pme->WriteMSR( escrPort, escr.flat ); + } + + void SetFiltering( CompareState compareEnable, CompareMethod compareMethod, uint8 threshold, EdgeState edgeEnable ) + { + cccr.Compare = compareEnable; + cccr.Complement = compareMethod; + cccr.Threshold = threshold; + cccr.Edge = edgeEnable; + pme->WriteMSR( cccrPort, cccr.flat ); + } + + void SetOverflowEnables( P4ForceOverflow overflowEnable, P4OverflowInterrupt overflowInterruptEnable ) + { + cccr.FORCE_OVF = overflowEnable; + cccr.OVF_PMI = overflowInterruptEnable; + pme->WriteMSR( cccrPort, cccr.flat ); + } + + void SetOverflow() + { + cccr.OVF = 1; + pme->WriteMSR( cccrPort, cccr.flat ); + } + + void ClearOverflow() + { + cccr.OVF = 0; + pme->WriteMSR( cccrPort, cccr.flat ); + } + + bool isOverflow() + { + CCCR cccr_temp; + pme->ReadMSR( cccrPort, &cccr_temp.flat ); + return cccr_temp.OVF; + } + + void SetCascade() + { + cccr.Cascade = 1; + pme->WriteMSR( cccrPort, cccr.flat ); + } + + void ClearCascade() + { + cccr.Cascade = 0; + pme->WriteMSR( cccrPort, cccr.flat ); + } +}; +#pragma warning( default : 4035 ) + +#include "EventMasks.h" +#include "EventModes.h" + +#endif // P4PERFORMANCECOUNTERS_H diff --git a/public/tier0/P5P6PerformanceCounters.h b/public/tier0/P5P6PerformanceCounters.h new file mode 100644 index 0000000..a765756 --- /dev/null +++ b/public/tier0/P5P6PerformanceCounters.h @@ -0,0 +1,225 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +// +//=============================================================================// +#ifndef P5P6PERFORMANCECOUNTERS_H +#define P5P6PERFORMANCECOUNTERS_H + +// defined for < Pentium 4 + +//--------------------------------------------------------------------------- +// Format of the performance event IDs within this header file in case you +// wish to add any additional events that may not be present here. +// +// BITS 0-8 Unit Mask, Unsed on P5 processors +// BIT 9 Set if event can be set on counter 0 +// BIT 10 Set if event can be set on counter 1 +// BITS 11-15 Unused Set to zero +// BITS 16-23 Event Select ID, Only bits 16-21 used on P5 Family +// BITS 24-27 Unused set to zero +// BITS 28-32 Process family that the event belong to, as returned by +// the CPUID instruction. +//--------------------------------------------------------------------------- + +//--------------------------------------------------------------------------- +// PENTIUM PERFORMANCE COUNTERS. +//--------------------------------------------------------------------------- +#define P5_DTCRD 0x50000300 //Data Cache Reads +#define P5_DWRIT 0x50010300 //Data Cache Writes +#define P5_DTTLB 0x50020300 //Data TLB Miss +#define P5_DTRMS 0x50030300 //Data Read Misses +#define P5_DWRMS 0x50040300 //Data Write Miss +#define P5_WHLCL 0x50050300 //Write (Hit) to M- or E-state line +#define P5_DCLWB 0x50060300 //Data Cache Lines Written Back +#define P5_DCSNP 0x50070300 //External Snoops +#define P5_DCSHT 0x50080300 //Data Cache Snoop Hits +#define P5_MAIBP 0x50090300 //Memory Access in Both Pipes +#define P5_BANKS 0x500A0300 //Bank Conflicts +#define P5_MISAL 0x500B0300 //Misaligned Data Memory Reference +#define P5_COCRD 0x500C0300 //Code Cache Reads +#define P5_COTLB 0x500D0300 //Code TLB Misses +#define P5_COCMS 0x500E0300 //Code Cache Misses +#define P5_ANYSG 0x500F0300 //Any Segment Register Loaded +#define P5_BRANC 0x50120300 //Branches +#define P5_BTBHT 0x50130300 //BTB Hits +#define P5_TBRAN 0x50140300 //Taken Branch or BTB hit +#define P5_PFLSH 0x50150300 //Pipeline flushes +#define P5_INSTR 0x50160300 //Instructions Executed +#define P5_INSTV 0x50170300 //Instructions Executed in the V-Pipe (Pairing) +#define P5_CLOCL 0x50180300 //Bus active +#define P5_PSDWR 0x50190300 //Full write buffers +#define P5_PSWDR 0x501A0300 //Waiting for Data Memory Read +#define P5_NCLSW 0x501B0300 //Clocks stalled writing an E or M state line +#define P5_IORWC 0x501D0300 //I/O Read or Write Cycle +#define P5_NOCMR 0x501E0300 //Non-Cacheable Memory Reads +#define P5_PSLDA 0x501F0300 //Clocks stalled due to AGI +#define P5_FLOPS 0x50220300 //Floating Point Operations +#define P5_DBGR0 0x50230300 //Breakpoint match on DR0 +#define P5_DBGR1 0x50240300 //Breakpoint match on DR1 +#define P5_DBGR2 0x50250300 //Breakpoint match on DR2 +#define P5_DBGR3 0x50260300 //Breakpoint match on DR3 +#define P5_HWINT 0x50270300 //Hardware interrupts +#define P5_DTRWR 0x50280300 //Data reads or writes +#define P5_DTRWM 0x50290300 //Data read or write miss +#define P5_BOLAT 0x502A0100 //Bus ownership latency +#define P5_BOTFR 0x502A0200 //Bus ownership transfer +#define P5_MMXA1 0x502B0100 //MMX Instruction Executed in U-pipe +#define P5_MMXA2 0x502B0200 //MMX Instruction Executed in V-pipe +#define P5_MMXMS 0x502C0100 //Cache M state line sharing +#define P5_MMSLS 0x502C0200 //Cache line sharing +#define P5_MMXB1 0x502D0100 //EMMS Instructions Executed +#define P5_MMXB2 0x502D0200 //Transition from MMX to FP instructions +#define P5_NOCMW 0x502E0200 //Non-Cacheable Memory Writes +#define P5_MMXC1 0x502F0100 //Saturated MMX Instructions Executed +#define P5_MMXC2 0x502F0200 //Saturations Performed +#define P5_MMXHS 0x50300100 //Cycles Not in HALT State +#define P5_MMXD2 0x50310100 //MMX Data Read +#define P5_MMXFP 0x50320100 //Floating Point Stalls +#define P5_MMXTB 0x50320200 //Taken Branches +#define P5_MMXD0 0x50330100 //D1 Starvation and FIFO Empty +#define P5_MMXD1 0x50330200 //D1 Starvation and one instruction in FIFO +#define P5_MMXE1 0x50340100 //MMX Data Writes +#define P5_MMXE2 0x50340200 //MMX Data Write Misses +#define P5_MMXWB 0x50350100 //Pipeline flushes, wrong branch prediction +#define P5_MMXWJ 0x50350200 //Pipeline flushes, branch prediction WB-stage +#define P5_MMXF1 0x50360100 //Misaligned MMX Data Memory Reference +#define P5_MMXF2 0x50360200 //Pipeline Stalled Waiting for MMX data read +#define P5_MMXRI 0x50370100 //Returns Predicted Incorrectly +#define P5_MMXRP 0x50370200 //Returns Predicted +#define P5_MMXG1 0x50380100 //MMX Multiply Unit Interlock +#define P5_MMXG2 0x50380200 //MOVD/MOVQ store stall, previous operation +#define P5_MMXRT 0x50390100 //Returns +#define P5_MMXRO 0x50390200 //RSB Overflows +#define P5_MMXBF 0x503A0100 //BTB False entries +#define P5_MMXBM 0x503A0200 //BTB misprediction on a Not-Taken Branch +#define P5_PXDWR 0x503B0100 //stalled due MMX Full write buffers +#define P5_PXZWR 0x503B0200 //stalled on MMX write to E or M state line + +#define P5_CLOCK 0x503F0300 //Special value to count clocks on P5 + + +//--------------------------------------------------------------------------- +// PENTIUM PRO / PENTIUM II PERFORMANCE COUNTERS. +//--------------------------------------------------------------------------- +#define P6_STRBB 0x60030300 //Store Buffer Block +#define P6_STBDC 0x60040300 //Store Buffer Drain Cycles +#define P6_MISMM 0x60050300 //Misaligned Data Memory Reference +#define P6_SEGLD 0x60060300 //Segment register loads +#define P6_FPOPE 0x60100100 //FP Computational Op. (COUNTER 0 ONLY) +#define P6_FPEOA 0x60110200 //FP Microcode Exceptions (COUNTER 1 ONLY) +#define P6_FMULT 0x60120200 //Multiplies (COUNTER 1 ONLY) +#define P6_FPDIV 0x60130200 //Divides (COUNTER 1 ONLY) +#define P6_DBUSY 0x60140200 //Cycles Divider Busy (COUNTER 1 ONLY) +#define P6_L2STR 0x60210300 //L2 address strobes => address bus utilization +#define P6_L2BBS 0x60220300 //Cycles L2 Bus Busy +#define P6_L2BBT 0x60230300 //Cycles L2 Bus Busy transferring data to CPU +#define P6_L2ALO 0x60240300 //L2 Lines Allocated +#define P6_L2MAL 0x60250300 //L2 M-state Lines Allocated +#define P6_L2CEV 0x60260300 //L2 Lines Evicted +#define P6_L2MEV 0x60270300 //L2 M-state Lines Evicted +#define P6_L2MCF 0x60280301 //L2 Cache Instruction Fetch Misses +#define P6_L2FET 0x6028030F //L2 Cache Instruction Fetches +#define P6_L2DRM 0x60290301 //L2 Cache Read Misses +#define P6_L2DMR 0x6029030F //L2 Cache Reads +#define P6_L2DWM 0x602A0301 //L2 Cache Write Misses +#define P6_L2DMW 0x602A030F //L2 Cache Writes +#define P6_L2CMS 0x602E0301 //L2 Cache Request Misses +#define P6_L2DCR 0x602E030F //L2 Cache Requests +#define P6_DMREF 0x60430300 //Data Memory References +#define P6_DCALO 0x6045030F //L1 Lines Allocated +#define P6_DCMAL 0x60460300 //L1 M-state Data Cache Lines Allocated +#define P6_DCMEV 0x60470300 //L1 M-state Data Cache Lines Evicted +#define P6_DCOUT 0x60480300 //L1 Misses outstanding +#define P6_TSMCD 0x60520300 //Time Self-Modifiying Code Detected +#define P6_BRWRA 0x60600300 //External Bus Cycles While Receive Active +#define P6_BRDCD 0x60600300 //External Bus Request Outstanding +#define P6_BRBNR 0x60610300 //External Bus Cycles While BNR Asserted +#define P6_BUSBS 0x60620300 //External Bus Cycles-DRDY Asserted (busy) +#define P6_BLOCK 0x60630300 //External Bus Cycles-LOCK signal asserted +#define P6_BBRCV 0x60640300 //External Bus Cycles-Processor receiving data +#define P6_BURST 0x60650300 //External Bus Burst Read Operations +#define P6_BRINV 0x60660300 //External Bus Read for Ownership Transaction +#define P6_BMLEV 0x60670300 //External Bus Writeback M-state Evicted +#define P6_BBIFT 0x60680300 //External Bus Burst Instruction Fetches +#define P6_BINVL 0x60690300 //External Bus Invalidate Transactions +#define P6_BPRBT 0x606A0300 //External Bus Partial Read Transactions +#define P6_BPTMO 0x606B0300 //External Bus Partial Memory Transactions +#define P6_BUSIO 0x606C0300 //External Bus I/O Bus Transactions +#define P6_BUSDF 0x606D0300 //External Bus Deferred Transactions +#define P6_BUSTB 0x606E0300 //External Bus Burst Transactions +#define P6_BMALL 0x606F0300 //External Bus Memory Transactions +#define P6_BSALL 0x60700300 //External Bus Transactions +#define P6_CLOCK 0x60790300 //Clockticks +#define P6_BRHIT 0x607A0300 //External Bus Cycles While HIT Asserted +#define P6_BRHTM 0x607B0300 //External Bus Cycles While HITM Asserted +#define P6_BRSST 0x607E0300 //External Bus Cycles While Snoop Stalled +#define P6_CMREF 0x60800300 //Total Instruction Fetches +#define P6_TOIFM 0x60810300 //Total Instruction Fetch Misses +#define P6_INTLB 0x60850300 //Instructions TLB Misses +#define P6_CSFET 0x60860300 //Cycles Instruction Fetch Stalled +#define P6_FTSTL 0x60870300 //Cycles Instruction Fetch stalled +#define P6_RSTAL 0x60A20300 //Resource Related Stalls +#define P6_MMXIE 0x60B00300 //MMX Instructions Executed +#define P6_SAISE 0x60B10300 //Saturated Arithmetic Instructions Executed +#define P6_PORT0 0x60B20301 //MMX micro-ops executed on Port 0 +#define P6_PORT1 0x60B20302 //MMX micro-ops executed on Port 1 +#define P6_PORT2 0x60B20304 //MMX micro-ops executed on Port 2 +#define P6_PORT3 0x60B20308 //MMX micro-ops executed on Port 3 +#define P6_MMXPA 0x60B30300 //MMX Packed Arithmetic +#define P6_MMXPM 0x60B30301 //MMX Packed Multiply +#define P6_MMXPS 0x60B30302 //MMX Packed Shift +#define P6_MMXPO 0x60B30304 //MMX Packed Operations +#define P6_MMXUO 0x60B30308 //MMX Unpacked Operations +#define P6_MMXPL 0x60B30310 //MMX Packed Logical +#define P6_INSTR 0x60C00300 //Instructions Retired +#define P6_FPOPS 0x60C10100 //FP operations retired (COUNTER 0 ONLY) +#define P6_UOPSR 0x60C20300 //Micro-Ops Retired +#define P6_BRRET 0x60C40300 //Branch Instructions Retired +#define P6_BRMSR 0x60C50300 //Branch Mispredictions Retired +#define P6_MASKD 0x60C60300 //Clocks while interrupts masked +#define P6_MSKPN 0x60C70300 //Clocks while interrupt is pending +#define P6_HWINT 0x60C80300 //Hardware Interrupts Received +#define P6_BTAKR 0x60C90300 //Taken Branch Retired +#define P6_BTAKM 0x60CA0300 //Taken Branch Mispredictions +#define P6_FPMMX 0x60CC0301 //Transitions from Floating Point to MMX +#define P6_MMXFP 0x60CC0300 //Transitions from MMX to Floating Point +#define P6_SIMDA 0x60CD0300 //SIMD Assists (EMMS Instructions Executed) +#define P6_MMXIR 0x60CE0300 //MMX Instructions Retired +#define P6_SAISR 0x60CF0300 //Saturated Arithmetic Instructions Retired +#define P6_INSTD 0x60D00300 //Instructions Decoded +#define P6_NPRTL 0x60D20300 //Renaming Stalls +#define P6_SRSES 0x60D40301 //Segment Rename Stalls - ES +#define P6_SRSDS 0x60D40302 //Segment Rename Stalls - DS +#define P6_SRSFS 0x60D40304 //Segment Rename Stalls - FS +#define P6_SRSGS 0x60D40308 //Segment Rename Stalls - GS +#define P6_SRSXS 0x60D4030F //Segment Rename Stalls - ES DS FS GS +#define P6_SRNES 0x60D50301 //Segment Renames - ES +#define P6_SRNDS 0x60D50302 //Segment Renames - DS +#define P6_SRNFS 0x60D50304 //Segment Renames - FS +#define P6_SRNGS 0x60D50308 //Segment Renames - GS +#define P6_SRNXS 0x60D5030F //Segment Renames - ES DS FS GS +#define P6_BRDEC 0x60E00300 //Branch Instructions Decoded +#define P6_BTBMS 0x60E20301 //BTB Misses +#define P6_RETDC 0x60E40300 //Bogus Branches +#define P6_BACLR 0x60E60300 //BACLEARS Asserted (Testing) + + + + + + +// INTEL +#define PENTIUM_FAMILY 5 // define for pentium +#define PENTIUMPRO_FAMILY 6 // define for pentium pro +#define PENTIUM4_FAMILY 15 // define for pentium 4 + + +// AMD +#define K6_FAMILY 5 +#define K8_FAMILY 6 +#define EXTENDED_FAMILY 15 // AMD 64 and AMD Opteron + +#endif // P5P6PERFORMANCECOUNTERS_H diff --git a/public/tier0/PMELib.h b/public/tier0/PMELib.h new file mode 100644 index 0000000..5eeb63c --- /dev/null +++ b/public/tier0/PMELib.h @@ -0,0 +1,192 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +// +//=============================================================================// +#ifndef PMELIB_H +#define PMELIB_H + +#include "Windows.h" +#include "tier0/platform.h" + +// Get rid of a bunch of STL warnings! +#pragma warning( push, 3 ) +#pragma warning( disable : 4018 ) + +#define VERSION "1.0.2" + +// uncomment this list to add some runtime checks +//#define PME_DEBUG + +#include "tier0/valve_off.h" +#include <string> +#include "tier0/valve_on.h" + +using namespace std; + +// RDTSC Instruction macro +#define RDTSC(var) var = __rdtsc() + +// RDPMC Instruction macro +#define RDPMC(counter, var) \ +_asm mov ecx, counter \ +_asm RDPMC \ +_asm mov DWORD PTR var,eax \ +_asm mov DWORD PTR var+4,edx + +// RDPMC Instruction macro, for performance counter 1 (ecx = 1) +#define RDPMC0(var) \ +_asm mov ecx, 0 \ +_asm RDPMC \ +_asm mov DWORD PTR var,eax \ +_asm mov DWORD PTR var+4,edx + +#define RDPMC1(var) \ +_asm mov ecx, 1 \ +_asm RDPMC \ +_asm mov DWORD PTR var,eax \ +_asm mov DWORD PTR var+4,edx + +#define EVENT_TYPE(mode) EventType##mode +#define EVENT_MASK(mode) EventMask##mode + +#include "ia32detect.h" + +enum ProcessPriority +{ + ProcessPriorityNormal, + ProcessPriorityHigh, +}; + +enum PrivilegeCapture +{ + OS_Only, // ring 0, kernel level + USR_Only, // app level + OS_and_USR, // all levels +}; + +enum CompareMethod +{ + CompareGreater, // + CompareLessEqual, // +}; + +enum EdgeState +{ + RisingEdgeDisabled, // + RisingEdgeEnabled, // +}; + +enum CompareState +{ + CompareDisable, // + CompareEnable, // +}; + +// Singletion Class +class PME : public ia32detect +{ +public: +//private: + + static PME* _singleton; + + HANDLE hFile; + bool bDriverOpen; + double m_CPUClockSpeed; + + //ia32detect detect; + HRESULT Init(); + HRESULT Close(); + +protected: + + PME() + { + hFile = NULL; + bDriverOpen = FALSE; + m_CPUClockSpeed = 0; + Init(); + } + +public: + + static PME* Instance(); // gives back a real object + + ~PME() + { + Close(); + } + + double GetCPUClockSpeedSlow( void ); + double GetCPUClockSpeedFast( void ); + + HRESULT SelectP5P6PerformanceEvent( uint32 dw_event, uint32 dw_counter, bool b_user, bool b_kernel ); + + HRESULT ReadMSR( uint32 dw_reg, int64 * pi64_value ); + HRESULT ReadMSR( uint32 dw_reg, uint64 * pi64_value ); + + HRESULT WriteMSR( uint32 dw_reg, const int64 & i64_value ); + HRESULT WriteMSR( uint32 dw_reg, const uint64 & i64_value ); + + void SetProcessPriority( ProcessPriority priority ) + { + switch( priority ) + { + case ProcessPriorityNormal: + { + SetPriorityClass (GetCurrentProcess(),NORMAL_PRIORITY_CLASS); + SetThreadPriority (GetCurrentThread(),THREAD_PRIORITY_NORMAL); + break; + } + case ProcessPriorityHigh: + { + SetPriorityClass (GetCurrentProcess(),REALTIME_PRIORITY_CLASS); + SetThreadPriority (GetCurrentThread(),THREAD_PRIORITY_HIGHEST); + break; + } + } + } + + //--------------------------------------------------------------------------- + // Return the family of the processor + //--------------------------------------------------------------------------- + CPUVendor GetVendor(void) + { + return vendor; + } + + int GetProcessorFamily(void) + { + return version.Family; + } + +#ifdef DBGFLAG_VALIDATE + void Validate( CValidator &validator, tchar *pchName ); // Validate our internal structures +#endif // DBGFLAG_VALIDATE + +}; + +#include "P5P6PerformanceCounters.h" +#include "P4PerformanceCounters.h" +#include "K8PerformanceCounters.h" + +enum PerfErrors +{ + E_UNKNOWN_CPU_VENDOR = -1, + E_BAD_COUNTER = -2, + E_UNKNOWN_CPU = -3, + E_CANT_OPEN_DRIVER = -4, + E_DRIVER_ALREADY_OPEN = -5, + E_DRIVER_NOT_OPEN = -6, + E_DISABLED = -7, + E_BAD_DATA = -8, + E_CANT_CLOSE = -9, + E_ILLEGAL_OPERATION = -10, +}; + +#pragma warning( pop ) + +#endif // PMELIB_H
\ No newline at end of file diff --git a/public/tier0/afxmem_override.cpp b/public/tier0/afxmem_override.cpp new file mode 100644 index 0000000..f5794b2 --- /dev/null +++ b/public/tier0/afxmem_override.cpp @@ -0,0 +1,456 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +// +//=============================================================================// +// File extracted from MFC due to symbol conflicts + +// This is a part of the Microsoft Foundation Classes C++ library. +// Copyright (C) Microsoft Corporation +// All rights reserved. +// +// This source code is only intended as a supplement to the +// Microsoft Foundation Classes Reference and related +// electronic documentation provided with the library. +// See these sources for detailed information regarding the +// Microsoft Foundation Classes product. + +#include "stdafx.h" + +#ifdef AFX_CORE1_SEG +#pragma code_seg(AFX_CORE1_SEG) +#endif + + +///////////////////////////////////////////////////////////////////////////// +// Debug memory globals and implementation helpers + +#ifdef _DEBUG // most of this file is for debugging + +void* __cdecl operator new(size_t nSize, int nType, LPCSTR lpszFileName, int nLine); +#if _MSC_VER >= 1210 +void* __cdecl operator new[](size_t nSize, int nType, LPCSTR lpszFileName, int nLine); +#endif + +///////////////////////////////////////////////////////////////////////////// +// test allocation routines + +void* PASCAL CObject::operator new(size_t nSize) +{ +#ifdef _AFX_NO_DEBUG_CRT + return ::operator new(nSize); +#else + return ::operator new(nSize, _AFX_CLIENT_BLOCK, NULL, 0); +#endif // _AFX_NO_DEBUG_CRT +} + +void PASCAL CObject::operator delete(void* p) +{ +#ifdef _AFX_NO_DEBUG_CRT + free(p); +#else + _free_dbg(p, _AFX_CLIENT_BLOCK); +#endif +} + +#if _MSC_VER >= 1200 +void PASCAL CObject::operator delete(void* p, void*) +{ +#ifdef _AFX_NO_DEBUG_CRT + free(p); +#else + _free_dbg(p, _AFX_CLIENT_BLOCK); +#endif +} +#endif + +#ifndef _AFX_NO_DEBUG_CRT + +void* __cdecl operator new(size_t nSize, LPCSTR lpszFileName, int nLine) +{ + return ::operator new(nSize, _NORMAL_BLOCK, lpszFileName, nLine); +} + +#if _MSC_VER >= 1210 +void* __cdecl operator new[](size_t nSize, LPCSTR lpszFileName, int nLine) +{ + return ::operator new[](nSize, _NORMAL_BLOCK, lpszFileName, nLine); +} +#endif + +#if _MSC_VER >= 1200 +void __cdecl operator delete(void* pData, LPCSTR /* lpszFileName */, + int /* nLine */) +{ + ::operator delete(pData); +} +#endif + +#if _MSC_VER >= 1210 +void __cdecl operator delete[](void* pData, LPCSTR /* lpszFileName */, + int /* nLine */) +{ + ::operator delete(pData); +} +#endif + +void* PASCAL +CObject::operator new(size_t nSize, LPCSTR lpszFileName, int nLine) +{ + return ::operator new(nSize, _AFX_CLIENT_BLOCK, lpszFileName, nLine); +} + +#if _MSC_VER >= 1200 +void PASCAL +CObject::operator delete(void *pObject, LPCSTR /* lpszFileName */, + int /* nLine */) +{ +#ifdef _AFX_NO_DEBUG_CRT + free(pObject); +#else + _free_dbg(pObject, _AFX_CLIENT_BLOCK); +#endif +} +#endif + +void* AFXAPI AfxAllocMemoryDebug(size_t nSize, BOOL bIsObject, LPCSTR lpszFileName, int nLine) +{ + return _malloc_dbg(nSize, bIsObject ? _AFX_CLIENT_BLOCK : _NORMAL_BLOCK, + lpszFileName, nLine); +} + +void AFXAPI AfxFreeMemoryDebug(void* pbData, BOOL bIsObject) +{ + _free_dbg(pbData, bIsObject ? _AFX_CLIENT_BLOCK : _NORMAL_BLOCK); +} + +///////////////////////////////////////////////////////////////////////////// +// allocation failure hook, tracking turn on + +BOOL AFXAPI _AfxDefaultAllocHook(size_t, BOOL, LONG) + { return TRUE; } + +AFX_STATIC_DATA AFX_ALLOC_HOOK pfnAllocHook = _AfxDefaultAllocHook; + +AFX_STATIC_DATA _CRT_ALLOC_HOOK pfnCrtAllocHook = NULL; +#if _MSC_VER >= 1200 +int __cdecl _AfxAllocHookProxy(int nAllocType, void * pvData, size_t nSize, + int nBlockUse, long lRequest, const unsigned char * szFilename, int nLine) +#else +int __cdecl _AfxAllocHookProxy(int nAllocType, void * pvData, size_t nSize, + int nBlockUse, long lRequest, const char * szFilename, int nLine) +#endif +{ +#if _MSC_VER >= 1200 + if (nAllocType != _HOOK_ALLOC) + return (pfnCrtAllocHook)(nAllocType, pvData, nSize, + nBlockUse, lRequest, (const unsigned char*) szFilename, nLine); + if ((pfnAllocHook)(nSize, _BLOCK_TYPE(nBlockUse) == _AFX_CLIENT_BLOCK, lRequest)) + return (pfnCrtAllocHook)(nAllocType, pvData, nSize, + nBlockUse, lRequest, (const unsigned char*) szFilename, nLine); +#else + if (nAllocType != _HOOK_ALLOC) + return (pfnCrtAllocHook)(nAllocType, pvData, nSize, + nBlockUse, lRequest, szFilename, nLine); + if ((pfnAllocHook)(nSize, _BLOCK_TYPE(nBlockUse) == _AFX_CLIENT_BLOCK, lRequest)) + return (pfnCrtAllocHook)(nAllocType, pvData, nSize, + nBlockUse, lRequest, szFilename, nLine); +#endif + return FALSE; +} + +AFX_ALLOC_HOOK AFXAPI AfxSetAllocHook(AFX_ALLOC_HOOK pfnNewHook) +{ + if (pfnCrtAllocHook == NULL) + pfnCrtAllocHook = _CrtSetAllocHook(_AfxAllocHookProxy); + + AFX_ALLOC_HOOK pfnOldHook = pfnAllocHook; + pfnAllocHook = pfnNewHook; + return pfnOldHook; +} + +// This can be set to TRUE to override all AfxEnableMemoryTracking calls, +// allowing all allocations, even MFC internal allocations to be tracked. +BOOL _afxMemoryLeakOverride = FALSE; + +BOOL AFXAPI AfxEnableMemoryTracking(BOOL bTrack) +{ + if (_afxMemoryLeakOverride) + return TRUE; + + int nOldState = _CrtSetDbgFlag(_CRTDBG_REPORT_FLAG); + if (bTrack) + _CrtSetDbgFlag(nOldState | _CRTDBG_ALLOC_MEM_DF); + else + _CrtSetDbgFlag(nOldState & ~_CRTDBG_ALLOC_MEM_DF); + return nOldState & _CRTDBG_ALLOC_MEM_DF; +} + +///////////////////////////////////////////////////////////////////////////// +// stop on a specific memory request + +// Obsolete API +void AFXAPI AfxSetAllocStop(LONG lRequestNumber) +{ + _CrtSetBreakAlloc(lRequestNumber); +} + +BOOL AFXAPI AfxCheckMemory() + // check all of memory (look for memory tromps) +{ + return _CrtCheckMemory(); +} + +// -- true if block of exact size, allocated on the heap +// -- set *plRequestNumber to request number (or 0) +BOOL AFXAPI AfxIsMemoryBlock(const void* pData, UINT nBytes, + LONG* plRequestNumber) +{ + return _CrtIsMemoryBlock(pData, nBytes, plRequestNumber, NULL, NULL); +} + +///////////////////////////////////////////////////////////////////////////// +// CMemoryState + +CMemoryState::CMemoryState() +{ + memset(this, 0, sizeof(*this)); +} + +void CMemoryState::UpdateData() +{ + for(int i = 0; i < nBlockUseMax; i++) + { + m_lCounts[i] = m_memState.lCounts[i]; + m_lSizes[i] = m_memState.lSizes[i]; + } + m_lHighWaterCount = m_memState.lHighWaterCount; + m_lTotalCount = m_memState.lTotalCount; +} + +// fills 'this' with the difference, returns TRUE if significant +BOOL CMemoryState::Difference(const CMemoryState& oldState, + const CMemoryState& newState) +{ + int nResult = _CrtMemDifference(&m_memState, &oldState.m_memState, &newState.m_memState); + UpdateData(); + return nResult != 0; +} + +void CMemoryState::DumpStatistics() const +{ + _CrtMemDumpStatistics(&m_memState); +} + +// -- fill with current memory state +void CMemoryState::Checkpoint() +{ + _CrtMemCheckpoint(&m_memState); + UpdateData(); +} + +// Dump objects created after this memory state was checkpointed +// Will dump all objects if this memory state wasn't checkpointed +// Dump all objects, report about non-objects also +// List request number in {} +void CMemoryState::DumpAllObjectsSince() const +{ + _CrtMemDumpAllObjectsSince(&m_memState); +} + +///////////////////////////////////////////////////////////////////////////// +// Enumerate all objects allocated in the diagnostic memory heap + +struct _AFX_ENUM_CONTEXT +{ + void (*m_pfn)(CObject*,void*); + void* m_pContext; +}; + +AFX_STATIC void _AfxDoForAllObjectsProxy(void* pObject, void* pContext) +{ + _AFX_ENUM_CONTEXT* p = (_AFX_ENUM_CONTEXT*)pContext; + (*p->m_pfn)((CObject*)pObject, p->m_pContext); +} + +void AFXAPI +AfxDoForAllObjects(void (AFX_CDECL *pfn)(CObject*, void*), void* pContext) +{ + if (pfn == NULL) + { + AfxThrowInvalidArgException(); + } + _AFX_ENUM_CONTEXT context; + context.m_pfn = pfn; + context.m_pContext = pContext; + _CrtDoForAllClientObjects(_AfxDoForAllObjectsProxy, &context); +} + +///////////////////////////////////////////////////////////////////////////// +// Automatic debug memory diagnostics + +BOOL AFXAPI AfxDumpMemoryLeaks() +{ + return _CrtDumpMemoryLeaks(); +} + +#endif // _AFX_NO_DEBUG_CRT +#endif // _DEBUG + +///////////////////////////////////////////////////////////////////////////// +// Non-diagnostic memory routines + +int AFX_CDECL AfxNewHandler(size_t /* nSize */) +{ + AfxThrowMemoryException(); +} + +#pragma warning(disable: 4273) + +#ifndef _AFXDLL +_PNH _afxNewHandler = &AfxNewHandler; +#endif + +_PNH AFXAPI AfxGetNewHandler(void) +{ +#ifdef _AFXDLL + AFX_MODULE_THREAD_STATE* pState = AfxGetModuleThreadState(); + return pState->m_pfnNewHandler; +#else + return _afxNewHandler; +#endif +} + +_PNH AFXAPI AfxSetNewHandler(_PNH pfnNewHandler) +{ +#ifdef _AFXDLL + AFX_MODULE_THREAD_STATE* pState = AfxGetModuleThreadState(); + _PNH pfnOldHandler = pState->m_pfnNewHandler; + pState->m_pfnNewHandler = pfnNewHandler; + return pfnOldHandler; +#else + _PNH pfnOldHandler = _afxNewHandler; + _afxNewHandler = pfnNewHandler; + return pfnOldHandler; +#endif +} + +AFX_STATIC_DATA const _PNH _pfnUninitialized = (_PNH)-1; + +void* __cdecl operator new(size_t nSize) +{ + void* pResult; +#ifdef _AFXDLL + _PNH pfnNewHandler = _pfnUninitialized; +#endif + for (;;) + { +#if !defined(_AFX_NO_DEBUG_CRT) && defined(_DEBUG) + pResult = _malloc_dbg(nSize, _NORMAL_BLOCK, NULL, 0); +#else + pResult = malloc(nSize); +#endif + if (pResult != NULL) + return pResult; + +#ifdef _AFXDLL + if (pfnNewHandler == _pfnUninitialized) + { + AFX_MODULE_THREAD_STATE* pState = AfxGetModuleThreadState(); + pfnNewHandler = pState->m_pfnNewHandler; + } + if (pfnNewHandler == NULL || (*pfnNewHandler)(nSize) == 0) + break; +#else + if (_afxNewHandler == NULL || (*_afxNewHandler)(nSize) == 0) + break; +#endif + } + return pResult; +} + +void __cdecl operator delete(void* p) +{ +#if !defined(_AFX_NO_DEBUG_CRT) && defined(_DEBUG) + _free_dbg(p, _NORMAL_BLOCK); +#else + free(p); +#endif +} + +#if _MSC_VER >= 1210 +void* __cdecl operator new[](size_t nSize) +{ + return ::operator new(nSize); +} + +void __cdecl operator delete[](void* p) +{ + ::operator delete(p); +} +#endif + +#ifdef _DEBUG + +void* __cdecl operator new(size_t nSize, int nType, LPCSTR lpszFileName, int nLine) +{ +#ifdef _AFX_NO_DEBUG_CRT + UNUSED_ALWAYS(nType); + UNUSED_ALWAYS(lpszFileName); + UNUSED_ALWAYS(nLine); + return ::operator new(nSize); +#else + void* pResult; +#ifdef _AFXDLL + _PNH pfnNewHandler = _pfnUninitialized; +#endif + for (;;) + { + pResult = _malloc_dbg(nSize, nType, lpszFileName, nLine); + if (pResult != NULL) + return pResult; + +#ifdef _AFXDLL + if (pfnNewHandler == _pfnUninitialized) + { + AFX_MODULE_THREAD_STATE* pState = AfxGetModuleThreadState(); + pfnNewHandler = pState->m_pfnNewHandler; + } + if (pfnNewHandler == NULL || (*pfnNewHandler)(nSize) == 0) + break; +#else + if (_afxNewHandler == NULL || (*_afxNewHandler)(nSize) == 0) + break; +#endif + } + return pResult; +#endif +} + +#if _MSC_VER >= 1700 +void __cdecl operator delete(void* p, int nType, LPCSTR /* lpszFileName */, int /* nLine */) +{ +#if !defined(_AFX_NO_DEBUG_CRT) && defined(_DEBUG) + _free_dbg(p, nType); +#else + free(p); +#endif +} +#endif // _MSC_VER >= 1200 + +#if _MSC_VER >= 1700 +void* __cdecl operator new[](size_t nSize, int nType, LPCSTR lpszFileName, int nLine) +{ + return ::operator new(nSize, nType, lpszFileName, nLine); +} +void __cdecl operator delete[](void* p, int nType, LPCSTR lpszFileName, int nLine) +{ + ::operator delete(p, nType, lpszFileName, nLine); +} +#endif // _MSC_VER >= 1210 + +#endif //_DEBUG + +///////////////////////////////////////////////////////////////////////////// diff --git a/public/tier0/annotations.h b/public/tier0/annotations.h new file mode 100644 index 0000000..7e0f469 --- /dev/null +++ b/public/tier0/annotations.h @@ -0,0 +1,84 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +#ifndef ANALYSIS_ANNOTATIONS_H +#define ANALYSIS_ANNOTATIONS_H + +#if _MSC_VER >= 1600 // VS 2010 and above. +//----------------------------------------------------------------------------- +// Upgrading important helpful warnings to errors +//----------------------------------------------------------------------------- +#pragma warning(error : 4789 ) // warning C4789: destination of memory copy is too small + +// Suppress some code analysis warnings +#ifdef _PREFAST_ +// Include the annotation header file. +#include <sal.h> + +// For temporarily suppressing warnings -- the warnings are suppressed for the next source line. +#define ANALYZE_SUPPRESS(wnum) __pragma(warning(suppress: wnum)) +#define ANALYZE_SUPPRESS2(wnum1, wnum2) __pragma(warning(supress: wnum1 wnum2)) +#define ANALYZE_SUPPRESS3(wnum1, wnum2, wnum3) __pragma(warning(suppress: wnum1 wnum2 wnum3)) +#define ANALYZE_SUPPRESS4(wnum1, wnum2, wnum3, wnum4) __pragma(warning(suppress: wnum1 wnum2 wnum3 wnum4)) + +// Tag all printf style format strings with this +#define PRINTF_FORMAT_STRING _Printf_format_string_ +#define SCANF_FORMAT_STRING _Scanf_format_string_impl_ +// Various macros for specifying the capacity of the buffer pointed +// to by a function parameter. Variations include in/out/inout, +// CAP (elements) versus BYTECAP (bytes), and null termination or +// not (_Z). +#define IN_Z _In_z_ +#define IN_CAP(x) _In_count_(x) +#define IN_BYTECAP(x) _In_bytecount_(x) +#define OUT_Z_CAP(x) _Out_z_cap_(x) +#define OUT_CAP(x) _Out_cap_(x) +#define OUT_CAP_C(x) _Out_cap_c_(x) // Output buffer with specified *constant* capacity in elements +#define OUT_BYTECAP(x) _Out_bytecap_(x) +#define OUT_Z_BYTECAP(x) _Out_z_bytecap_(x) +#define INOUT_BYTECAP(x) _Inout_bytecap_(x) +#define INOUT_Z_CAP(x) _Inout_z_cap_(x) +#define INOUT_Z_BYTECAP(x) _Inout_z_bytecap_(x) +// These macros are use for annotating array reference parameters, typically used in functions +// such as V_strcpy_safe. Because they are array references the capacity is already known. +#if _MSC_VER >= 1700 +#define IN_Z_ARRAY _Pre_z_ +#define OUT_Z_ARRAY _Post_z_ +#define INOUT_Z_ARRAY _Prepost_z_ +#else +#define IN_Z_ARRAY _Deref_pre_z_ +#define OUT_Z_ARRAY _Deref_post_z_ +#define INOUT_Z_ARRAY _Deref_prepost_z_ +#endif // _MSC_VER >= 1700 +// Used for annotating functions to describe their return types. +#define MUST_CHECK_RETURN _Check_return_ +// Use the macros above to annotate string functions that fill buffers as shown here, +// in order to give VS's /analyze more opportunities to find bugs. +// void V_wcsncpy( OUT_Z_BYTECAP(maxLenInBytes) wchar_t *pDest, wchar_t const *pSrc, int maxLenInBytes ); +// int V_snwprintf( OUT_Z_CAP(maxLenInCharacters) wchar_t *pDest, int maxLenInCharacters, PRINTF_FORMAT_STRING const wchar_t *pFormat, ... ); + +#endif // _PREFAST_ +#endif // _MSC_VER >= 1600 // VS 2010 and above. + +#ifndef ANALYZE_SUPPRESS +#define ANALYZE_SUPPRESS(wnum) +#define ANALYZE_SUPPRESS2(wnum1, wnum2) +#define ANALYZE_SUPPRESS3(wnum1, wnum2, wnum3) +#define ANALYZE_SUPPRESS4(wnum1, wnum2, wnum3, wnum4) +#define PRINTF_FORMAT_STRING +#define SCANF_FORMAT_STRING +#define IN_Z +#define IN_CAP(x) +#define IN_BYTECAP(x) +#define OUT_Z_CAP(x) +#define OUT_CAP(x) +#define OUT_CAP_C(x) +#define OUT_BYTECAP(x) +#define OUT_Z_BYTECAP(x) +#define INOUT_BYTECAP(x) +#define INOUT_Z_CAP(x) +#define INOUT_Z_BYTECAP(x) +#define OUT_Z_ARRAY +#define INOUT_Z_ARRAY +#define MUST_CHECK_RETURN +#endif + +#endif // ANALYSIS_ANNOTATIONS_H diff --git a/public/tier0/basetypes.h b/public/tier0/basetypes.h new file mode 100644 index 0000000..22ce51b --- /dev/null +++ b/public/tier0/basetypes.h @@ -0,0 +1,398 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +//=============================================================================// + +#ifndef BASETYPES_H +#define BASETYPES_H + +#include "commonmacros.h" +#include "wchartypes.h" + +#include "tier0/valve_off.h" + +#ifdef _WIN32 +#pragma once +#endif + + +// This is a trick to get the DLL extension off the -D option on the command line. +#define DLLExtTokenPaste(x) #x +#define DLLExtTokenPaste2(x) DLLExtTokenPaste(x) +#define DLL_EXT_STRING DLLExtTokenPaste2( _DLL_EXT ) + + +#include "protected_things.h" + +// There's a different version of this file in the xbox codeline +// so the PC version built in the xbox branch includes things like +// tickrate changes. +#include "xbox_codeline_defines.h" + +#ifdef IN_XBOX_CODELINE +#define XBOX_CODELINE_ONLY() +#else +#define XBOX_CODELINE_ONLY() Error_Compiling_Code_Only_Valid_in_Xbox_Codeline +#endif + +// stdio.h +#ifndef NULL +#define NULL 0 +#endif + + +#ifdef POSIX +#include <stdint.h> +#endif + +#define ExecuteNTimes( nTimes, x ) \ + { \ + static int __executeCount=0;\ + if ( __executeCount < nTimes )\ + { \ + x; \ + ++__executeCount; \ + } \ + } + + +#define ExecuteOnce( x ) ExecuteNTimes( 1, x ) + + +template <typename T> +inline T AlignValue( T val, uintptr_t alignment ) +{ + return (T)( ( (uintptr_t)val + alignment - 1 ) & ~( alignment - 1 ) ); +} + + +// Pad a number so it lies on an N byte boundary. +// So PAD_NUMBER(0,4) is 0 and PAD_NUMBER(1,4) is 4 +#define PAD_NUMBER(number, boundary) \ + ( ((number) + ((boundary)-1)) / (boundary) ) * (boundary) + +// In case this ever changes +#if !defined(M_PI) && !defined(HAVE_M_PI) +#define M_PI 3.14159265358979323846 +#endif + +#include "valve_minmax_on.h" + +// #define COMPILETIME_MAX and COMPILETIME_MIN for max/min in constant expressions +#define COMPILETIME_MIN( a, b ) ( ( ( a ) < ( b ) ) ? ( a ) : ( b ) ) +#define COMPILETIME_MAX( a, b ) ( ( ( a ) > ( b ) ) ? ( a ) : ( b ) ) +#ifndef MIN +#define MIN( a, b ) ( ( ( a ) < ( b ) ) ? ( a ) : ( b ) ) +#endif + +#ifndef MAX +#define MAX( a, b ) ( ( ( a ) > ( b ) ) ? ( a ) : ( b ) ) +#endif + +#ifdef __cplusplus + +// This is the preferred clamp operator. Using the clamp macro can lead to +// unexpected side-effects or more expensive code. Even the clamp (all +// lower-case) function can generate more expensive code because of the +// mixed types involved. +template< class T > +T Clamp( T const &val, T const &minVal, T const &maxVal ) +{ + if( val < minVal ) + return minVal; + else if( val > maxVal ) + return maxVal; + else + return val; +} + +// This is the preferred Min operator. Using the MIN macro can lead to unexpected +// side-effects or more expensive code. +template< class T > +T Min( T const &val1, T const &val2 ) +{ + return val1 < val2 ? val1 : val2; +} + +// This is the preferred Max operator. Using the MAX macro can lead to unexpected +// side-effects or more expensive code. +template< class T > +T Max( T const &val1, T const &val2 ) +{ + return val1 > val2 ? val1 : val2; +} + +#endif + +#ifndef FALSE +#define FALSE 0 +#define TRUE (!FALSE) +#endif + + +#ifndef DONT_DEFINE_BOOL // Needed for Cocoa stuff to compile. +typedef int BOOL; +#endif + +typedef int qboolean; +typedef unsigned long ULONG; +typedef unsigned char BYTE; +typedef unsigned char byte; +typedef unsigned short word; +#ifdef _WIN32 +typedef wchar_t ucs2; // under windows wchar_t is ucs2 +#else +typedef unsigned short ucs2; +#endif + +enum ThreeState_t +{ + TRS_FALSE, + TRS_TRUE, + TRS_NONE, +}; + +typedef float vec_t; + +#if defined(__GNUC__) +#define fpmin __builtin_fminf +#define fpmax __builtin_fmaxf +#elif !defined(_X360) +#define fpmin min +#define fpmax max +#endif + + +//----------------------------------------------------------------------------- +// look for NANs, infinities, and underflows. +// This assumes the ANSI/IEEE 754-1985 standard +//----------------------------------------------------------------------------- + +inline unsigned long& FloatBits( vec_t& f ) +{ + return *reinterpret_cast<unsigned long*>(&f); +} + +inline unsigned long const& FloatBits( vec_t const& f ) +{ + return *reinterpret_cast<unsigned long const*>(&f); +} + +inline vec_t BitsToFloat( unsigned long i ) +{ + return *reinterpret_cast<vec_t*>(&i); +} + +inline bool IsFinite( vec_t f ) +{ + return ((FloatBits(f) & 0x7F800000) != 0x7F800000); +} + +inline unsigned long FloatAbsBits( vec_t f ) +{ + return FloatBits(f) & 0x7FFFFFFF; +} + +// Given today's processors, I cannot think of any circumstance +// where bit tricks would be faster than fabs. henryg 8/16/2011 +#ifdef _MSC_VER +#ifndef _In_ +#define _In_ +#endif +extern "C" float fabsf(_In_ float); +#else +#include <math.h> +#endif + +inline float FloatMakeNegative( vec_t f ) +{ + return -fabsf(f); +} + +inline float FloatMakePositive( vec_t f ) +{ + return fabsf(f); +} + +inline float FloatNegate( vec_t f ) +{ + return -f; +} + + +#define FLOAT32_NAN_BITS (unsigned long)0x7FC00000 // not a number! +#define FLOAT32_NAN BitsToFloat( FLOAT32_NAN_BITS ) + +#define VEC_T_NAN FLOAT32_NAN + + + +// FIXME: why are these here? Hardly anyone actually needs them. +struct color24 +{ + byte r, g, b; +}; + +typedef struct color32_s +{ + bool operator!=( const struct color32_s &other ) const; + + byte r, g, b, a; +} color32; + +inline bool color32::operator!=( const color32 &other ) const +{ + return r != other.r || g != other.g || b != other.b || a != other.a; +} + +struct colorVec +{ + unsigned r, g, b, a; +}; + + +#ifndef NOTE_UNUSED +#define NOTE_UNUSED(x) (void)(x) // for pesky compiler / lint warnings +#endif + +struct vrect_t +{ + int x,y,width,height; + vrect_t *pnext; +}; + + +//----------------------------------------------------------------------------- +// MaterialRect_t struct - used for DrawDebugText +//----------------------------------------------------------------------------- +struct Rect_t +{ + int x, y; + int width, height; +}; + + +//----------------------------------------------------------------------------- +// Interval, used by soundemittersystem + the game +//----------------------------------------------------------------------------- +struct interval_t +{ + float start; + float range; +}; + + +//----------------------------------------------------------------------------- +// Declares a type-safe handle type; you can't assign one handle to the next +//----------------------------------------------------------------------------- + +// 32-bit pointer handles. + +// Typesafe 8-bit and 16-bit handles. +template< class HandleType > +class CBaseIntHandle +{ +public: + + inline bool operator==( const CBaseIntHandle &other ) { return m_Handle == other.m_Handle; } + inline bool operator!=( const CBaseIntHandle &other ) { return m_Handle != other.m_Handle; } + + // Only the code that doles out these handles should use these functions. + // Everyone else should treat them as a transparent type. + inline HandleType GetHandleValue() { return m_Handle; } + inline void SetHandleValue( HandleType val ) { m_Handle = val; } + + typedef HandleType HANDLE_TYPE; + +protected: + + HandleType m_Handle; +}; + +template< class DummyType > +class CIntHandle16 : public CBaseIntHandle< unsigned short > +{ +public: + inline CIntHandle16() {} + + static inline CIntHandle16<DummyType> MakeHandle( HANDLE_TYPE val ) + { + return CIntHandle16<DummyType>( val ); + } + +protected: + inline CIntHandle16( HANDLE_TYPE val ) + { + m_Handle = val; + } +}; + + +template< class DummyType > +class CIntHandle32 : public CBaseIntHandle< unsigned long > +{ +public: + inline CIntHandle32() {} + + static inline CIntHandle32<DummyType> MakeHandle( HANDLE_TYPE val ) + { + return CIntHandle32<DummyType>( val ); + } + +protected: + inline CIntHandle32( HANDLE_TYPE val ) + { + m_Handle = val; + } +}; + + +// NOTE: This macro is the same as windows uses; so don't change the guts of it +#define DECLARE_HANDLE_16BIT(name) typedef CIntHandle16< struct name##__handle * > name; +#define DECLARE_HANDLE_32BIT(name) typedef CIntHandle32< struct name##__handle * > name; + +#define DECLARE_POINTER_HANDLE(name) struct name##__ { int unused; }; typedef struct name##__ *name +#define FORWARD_DECLARE_HANDLE(name) typedef struct name##__ *name + +// @TODO: Find a better home for this +#if !defined(_STATIC_LINKED) && !defined(PUBLISH_DLL_SUBSYSTEM) +// for platforms built with dynamic linking, the dll interface does not need spoofing +#define PUBLISH_DLL_SUBSYSTEM() +#endif + +#define UID_PREFIX generated_id_ +#define UID_CAT1(a,c) a ## c +#define UID_CAT2(a,c) UID_CAT1(a,c) +#define EXPAND_CONCAT(a,c) UID_CAT1(a,c) +#ifdef _MSC_VER +#define UNIQUE_ID UID_CAT2(UID_PREFIX,__COUNTER__) +#else +#define UNIQUE_ID UID_CAT2(UID_PREFIX,__LINE__) +#endif + +// this allows enumerations to be used as flags, and still remain type-safe! +#define DEFINE_ENUM_BITWISE_OPERATORS( Type ) \ + inline Type operator| ( Type a, Type b ) { return Type( int( a ) | int( b ) ); } \ + inline Type operator& ( Type a, Type b ) { return Type( int( a ) & int( b ) ); } \ + inline Type operator^ ( Type a, Type b ) { return Type( int( a ) ^ int( b ) ); } \ + inline Type operator<< ( Type a, int b ) { return Type( int( a ) << b ); } \ + inline Type operator>> ( Type a, int b ) { return Type( int( a ) >> b ); } \ + inline Type &operator|= ( Type &a, Type b ) { return a = a | b; } \ + inline Type &operator&= ( Type &a, Type b ) { return a = a & b; } \ + inline Type &operator^= ( Type &a, Type b ) { return a = a ^ b; } \ + inline Type &operator<<=( Type &a, int b ) { return a = a << b; } \ + inline Type &operator>>=( Type &a, int b ) { return a = a >> b; } \ + inline Type operator~( Type a ) { return Type( ~int( a ) ); } + +// defines increment/decrement operators for enums for easy iteration +#define DEFINE_ENUM_INCREMENT_OPERATORS( Type ) \ + inline Type &operator++( Type &a ) { return a = Type( int( a ) + 1 ); } \ + inline Type &operator--( Type &a ) { return a = Type( int( a ) - 1 ); } \ + inline Type operator++( Type &a, int ) { Type t = a; ++a; return t; } \ + inline Type operator--( Type &a, int ) { Type t = a; --a; return t; } + +#include "tier0/valve_on.h" + +#endif // BASETYPES_H diff --git a/public/tier0/commonmacros.h b/public/tier0/commonmacros.h new file mode 100644 index 0000000..34aec40 --- /dev/null +++ b/public/tier0/commonmacros.h @@ -0,0 +1,174 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +// +//=============================================================================// +#ifndef COMMONMACROS_H +#define COMMONMACROS_H + +#ifdef _WIN32 +#pragma once +#endif + +#include "tier0/platform.h" + +// ------------------------------------------------------- +// +// commonmacros.h +// +// This should contain ONLY general purpose macros that are +// appropriate for use in engine/launcher/all tools +// +// ------------------------------------------------------- + +// Makes a 4-byte "packed ID" int out of 4 characters +#define MAKEID(d,c,b,a) ( ((int)(a) << 24) | ((int)(b) << 16) | ((int)(c) << 8) | ((int)(d)) ) + +// Compares a string with a 4-byte packed ID constant +#define STRING_MATCHES_ID( p, id ) ( (*((int *)(p)) == (id) ) ? true : false ) +#define ID_TO_STRING( id, p ) ( (p)[3] = (((id)>>24) & 0xFF), (p)[2] = (((id)>>16) & 0xFF), (p)[1] = (((id)>>8) & 0xFF), (p)[0] = (((id)>>0) & 0xFF) ) + +#define SETBITS(iBitVector, bits) ((iBitVector) |= (bits)) +#define CLEARBITS(iBitVector, bits) ((iBitVector) &= ~(bits)) +#define FBitSet(iBitVector, bits) ((iBitVector) & (bits)) + +template <typename T> +inline bool IsPowerOfTwo( T value ) +{ + return (value & ( value - (T)1 )) == (T)0; +} + +#ifndef REFERENCE +#define REFERENCE(arg) ((void)arg) +#endif + +#define CONST_INTEGER_AS_STRING(x) #x //Wraps the integer in quotes, allowing us to form constant strings with it +#define __HACK_LINE_AS_STRING__(x) CONST_INTEGER_AS_STRING(x) //__LINE__ can only be converted to an actual number by going through this, otherwise the output is literally "__LINE__" +#define __LINE__AS_STRING __HACK_LINE_AS_STRING__(__LINE__) //Gives you the line number in constant string form + +// Using ARRAYSIZE implementation from winnt.h: +#ifdef ARRAYSIZE +#undef ARRAYSIZE +#endif + +// Return the number of elements in a statically sized array. +// DWORD Buffer[100]; +// RTL_NUMBER_OF(Buffer) == 100 +// This is also popularly known as: NUMBER_OF, ARRSIZE, _countof, NELEM, etc. +// +#define RTL_NUMBER_OF_V1(A) (sizeof(A)/sizeof((A)[0])) + +#if defined(__cplusplus) && \ + !defined(MIDL_PASS) && \ + !defined(RC_INVOKED) && \ + (_MSC_FULL_VER >= 13009466) && \ + !defined(SORTPP_PASS) + +// From crtdefs.h +#if !defined(UNALIGNED) +#if defined(_M_IA64) || defined(_M_AMD64) +#define UNALIGNED __unaligned +#else +#define UNALIGNED +#endif +#endif + +// RtlpNumberOf is a function that takes a reference to an array of N Ts. +// +// typedef T array_of_T[N]; +// typedef array_of_T &reference_to_array_of_T; +// +// RtlpNumberOf returns a pointer to an array of N chars. +// We could return a reference instead of a pointer but older compilers do not accept that. +// +// typedef char array_of_char[N]; +// typedef array_of_char *pointer_to_array_of_char; +// +// sizeof(array_of_char) == N +// sizeof(*pointer_to_array_of_char) == N +// +// pointer_to_array_of_char RtlpNumberOf(reference_to_array_of_T); +// +// We never even call RtlpNumberOf, we just take the size of dereferencing its return type. +// We do not even implement RtlpNumberOf, we just decare it. +// +// Attempts to pass pointers instead of arrays to this macro result in compile time errors. +// That is the point. +extern "C++" // templates cannot be declared to have 'C' linkage +template <typename T, size_t N> +char (*RtlpNumberOf( UNALIGNED T (&)[N] ))[N]; + +#ifdef _PREFAST_ +// The +0 is so that we can go: +// size = ARRAYSIZE(array) * sizeof(array[0]) without triggering a /analyze +// warning about multiplying sizeof. +#define RTL_NUMBER_OF_V2(A) (sizeof(*RtlpNumberOf(A))+0) +#else +#define RTL_NUMBER_OF_V2(A) (sizeof(*RtlpNumberOf(A))) +#endif + +// This does not work with: +// +// void Foo() +// { +// struct { int x; } y[2]; +// RTL_NUMBER_OF_V2(y); // illegal use of anonymous local type in template instantiation +// } +// +// You must instead do: +// +// struct Foo1 { int x; }; +// +// void Foo() +// { +// Foo1 y[2]; +// RTL_NUMBER_OF_V2(y); // ok +// } +// +// OR +// +// void Foo() +// { +// struct { int x; } y[2]; +// RTL_NUMBER_OF_V1(y); // ok +// } +// +// OR +// +// void Foo() +// { +// struct { int x; } y[2]; +// _ARRAYSIZE(y); // ok +// } + +#else +#define RTL_NUMBER_OF_V2(A) RTL_NUMBER_OF_V1(A) +#endif + +// ARRAYSIZE is more readable version of RTL_NUMBER_OF_V2 +// _ARRAYSIZE is a version useful for anonymous types +#define ARRAYSIZE(A) RTL_NUMBER_OF_V2(A) +#define _ARRAYSIZE(A) RTL_NUMBER_OF_V1(A) + +#define Q_ARRAYSIZE(p) ARRAYSIZE(p) +#define V_ARRAYSIZE(p) ARRAYSIZE(p) + +template< typename IndexType, typename T, unsigned int N > +IndexType ClampedArrayIndex( const T (&buffer)[N], IndexType index ) +{ + NOTE_UNUSED( buffer ); + return clamp( index, 0, (IndexType)N - 1 ); +} + +template< typename T, unsigned int N > +T ClampedArrayElement( const T (&buffer)[N], unsigned int uIndex ) +{ + // Put index in an unsigned type to halve the clamping. + if ( uIndex >= N ) + uIndex = N - 1; + return buffer[ uIndex ]; +} + +#endif // COMMONMACROS_H diff --git a/public/tier0/cpumonitoring.h b/public/tier0/cpumonitoring.h new file mode 100644 index 0000000..e6d1c9c --- /dev/null +++ b/public/tier0/cpumonitoring.h @@ -0,0 +1,33 @@ +#ifndef CPU_MONITORING_H +#define CPU_MONITORING_H + +/* +This header defines functions and structures for controlling the measurement of CPU frequency +in order to detect thermal throttling. For details see the associated source file. +*/ + +struct CPUFrequencyResults +{ + double m_timeStamp; // Time (from Plat_FloatTime) when the measurements were made. + float m_GHz; + float m_percentage; + float m_lowestPercentage; +}; + +// Call this to get results. +// When CPU monitoring is 'disabled' it may still be running at a low frequency, +// for OGS purposes or for proactively warning users of problems. If fGetDisabledResults +// is true then results will be returned when disabled (if available). +PLATFORM_INTERFACE CPUFrequencyResults GetCPUFrequencyResults( bool fGetDisabledResults = false ); + +// Call this to set the monitoring frequency. Intervals of 2-5 seconds (2,000 to 5,000 ms) +// are recommended. An interval of zero will disable CPU monitoring. Short delays (below +// about 300 ms) will be rounded up. +PLATFORM_INTERFACE void SetCPUMonitoringInterval( unsigned nDelayMilliseconds ); + +// Warn with increasing strident colors when CPU percentages go below these levels. +// They are const int instead of float because const float in C++ is stupid. +const int kCPUMonitoringWarning1 = 80; +const int kCPUMonitoringWarning2 = 50; + +#endif diff --git a/public/tier0/dbg.h b/public/tier0/dbg.h new file mode 100644 index 0000000..1fff938 --- /dev/null +++ b/public/tier0/dbg.h @@ -0,0 +1,820 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +// +//=============================================================================// +#ifndef DBG_H +#define DBG_H + +#ifdef _WIN32 +#pragma once +#endif + +#include "basetypes.h" +#include "dbgflag.h" +#include "platform.h" +#include <math.h> +#include <stdio.h> +#include <stdarg.h> + +#ifdef POSIX +#define __cdecl +#endif + +//----------------------------------------------------------------------------- +// dll export stuff +//----------------------------------------------------------------------------- +#ifndef STATIC_TIER0 + +#ifdef TIER0_DLL_EXPORT +#define DBG_INTERFACE DLL_EXPORT +#define DBG_OVERLOAD DLL_GLOBAL_EXPORT +#define DBG_CLASS DLL_CLASS_EXPORT +#else +#define DBG_INTERFACE DLL_IMPORT +#define DBG_OVERLOAD DLL_GLOBAL_IMPORT +#define DBG_CLASS DLL_CLASS_IMPORT +#endif + +#else // BUILD_AS_DLL + +#define DBG_INTERFACE extern +#define DBG_OVERLOAD +#define DBG_CLASS +#endif // BUILD_AS_DLL + + +class Color; + + +//----------------------------------------------------------------------------- +// Usage model for the Dbg library +// +// 1. Spew. +// +// Spew can be used in a static and a dynamic mode. The static +// mode allows us to display assertions and other messages either only +// in debug builds, or in non-release builds. The dynamic mode allows us to +// turn on and off certain spew messages while the application is running. +// +// Static Spew messages: +// +// Assertions are used to detect and warn about invalid states +// Spews are used to display a particular status/warning message. +// +// To use an assertion, use +// +// Assert( (f == 5) ); +// AssertMsg( (f == 5), ("F needs to be %d here!\n", 5) ); +// AssertFunc( (f == 5), BadFunc() ); +// AssertEquals( f, 5 ); +// AssertFloatEquals( f, 5.0f, 1e-3 ); +// +// The first will simply report that an assertion failed on a particular +// code file and line. The second version will display a print-f formatted message +// along with the file and line, the third will display a generic message and +// will also cause the function BadFunc to be executed, and the last two +// will report an error if f is not equal to 5 (the last one asserts within +// a particular tolerance). +// +// To use a warning, use +// +// Warning("Oh I feel so %s all over\n", "yummy"); +// +// Warning will do its magic in only Debug builds. To perform spew in *all* +// builds, use RelWarning. +// +// Three other spew types, Msg, Log, and Error, are compiled into all builds. +// These error types do *not* need two sets of parenthesis. +// +// Msg( "Isn't this exciting %d?", 5 ); +// Error( "I'm just thrilled" ); +// +// Dynamic Spew messages +// +// It is possible to dynamically turn spew on and off. Dynamic spew is +// identified by a spew group and priority level. To turn spew on for a +// particular spew group, use SpewActivate( "group", level ). This will +// cause all spew in that particular group with priority levels <= the +// level specified in the SpewActivate function to be printed. Use DSpew +// to perform the spew: +// +// DWarning( "group", level, "Oh I feel even yummier!\n" ); +// +// Priority level 0 means that the spew will *always* be printed, and group +// '*' is the default spew group. If a DWarning is encountered using a group +// whose priority has not been set, it will use the priority of the default +// group. The priority of the default group is initially set to 0. +// +// Spew output +// +// The output of the spew system can be redirected to an externally-supplied +// function which is responsible for outputting the spew. By default, the +// spew is simply printed using printf. +// +// To redirect spew output, call SpewOutput. +// +// SpewOutputFunc( OutputFunc ); +// +// This will cause OutputFunc to be called every time a spew message is +// generated. OutputFunc will be passed a spew type and a message to print. +// It must return a value indicating whether the debugger should be invoked, +// whether the program should continue running, or whether the program +// should abort. +// +// 2. Code activation +// +// To cause code to be run only in debug builds, use DBG_CODE: +// An example is below. +// +// DBG_CODE( +// { +// int x = 5; +// ++x; +// } +// ); +// +// Code can be activated based on the dynamic spew groups also. Use +// +// DBG_DCODE( "group", level, +// { int x = 5; ++x; } +// ); +// +// 3. Breaking into the debugger. +// +// To cause an unconditional break into the debugger in debug builds only, use DBG_BREAK +// +// DBG_BREAK(); +// +// You can force a break in any build (release or debug) using +// +// DebuggerBreak(); +//----------------------------------------------------------------------------- + +/* Various types of spew messages */ +// I'm sure you're asking yourself why SPEW_ instead of DBG_ ? +// It's because DBG_ is used all over the place in windows.h +// For example, DBG_CONTINUE is defined. Feh. +enum SpewType_t +{ + SPEW_MESSAGE = 0, + SPEW_WARNING, + SPEW_ASSERT, + SPEW_ERROR, + SPEW_LOG, + + SPEW_TYPE_COUNT +}; + +enum SpewRetval_t +{ + SPEW_DEBUGGER = 0, + SPEW_CONTINUE, + SPEW_ABORT +}; + +/* type of externally defined function used to display debug spew */ +typedef SpewRetval_t (*SpewOutputFunc_t)( SpewType_t spewType, const tchar *pMsg ); + +/* Used to redirect spew output */ +DBG_INTERFACE void SpewOutputFunc( SpewOutputFunc_t func ); + +/* Used to get the current spew output function */ +DBG_INTERFACE SpewOutputFunc_t GetSpewOutputFunc( void ); + +/* This is the default spew fun, which is used if you don't specify one */ +DBG_INTERFACE SpewRetval_t DefaultSpewFunc( SpewType_t type, const tchar *pMsg ); + +/* Same as the default spew func, but returns SPEW_ABORT for asserts */ +DBG_INTERFACE SpewRetval_t DefaultSpewFuncAbortOnAsserts( SpewType_t type, const tchar *pMsg ); + +/* Should be called only inside a SpewOutputFunc_t, returns groupname, level, color */ +DBG_INTERFACE const tchar* GetSpewOutputGroup( void ); +DBG_INTERFACE int GetSpewOutputLevel( void ); +DBG_INTERFACE const Color* GetSpewOutputColor( void ); + +/* Used to manage spew groups and subgroups */ +DBG_INTERFACE void SpewActivate( const tchar* pGroupName, int level ); +DBG_INTERFACE bool IsSpewActive( const tchar* pGroupName, int level ); + +/* Used to display messages, should never be called directly. */ +DBG_INTERFACE void _SpewInfo( SpewType_t type, const tchar* pFile, int line ); +DBG_INTERFACE SpewRetval_t _SpewMessage( PRINTF_FORMAT_STRING const tchar* pMsg, ... ) FMTFUNCTION( 1, 2 ); +DBG_INTERFACE SpewRetval_t _DSpewMessage( const tchar *pGroupName, int level, PRINTF_FORMAT_STRING const tchar* pMsg, ... ) FMTFUNCTION( 3, 4 ); +DBG_INTERFACE SpewRetval_t ColorSpewMessage( SpewType_t type, const Color *pColor, PRINTF_FORMAT_STRING const tchar* pMsg, ... ) FMTFUNCTION( 3, 4 ); +DBG_INTERFACE void _ExitOnFatalAssert( const tchar* pFile, int line ); +DBG_INTERFACE bool ShouldUseNewAssertDialog(); + +DBG_INTERFACE bool SetupWin32ConsoleIO(); + +// Returns true if they want to break in the debugger. +DBG_INTERFACE bool DoNewAssertDialog( const tchar *pFile, int line, const tchar *pExpression ); + +// Allows the assert dialogs to be turned off from code +DBG_INTERFACE bool AreAllAssertsDisabled(); +DBG_INTERFACE void SetAllAssertsDisabled( bool bAssertsEnabled ); + +// Provides a callback that is called on asserts regardless of spew levels +typedef void (*AssertFailedNotifyFunc_t)( const char *pchFile, int nLine, const char *pchMessage ); +DBG_INTERFACE void SetAssertFailedNotifyFunc( AssertFailedNotifyFunc_t func ); +DBG_INTERFACE void CallAssertFailedNotifyFunc( const char *pchFile, int nLine, const char *pchMessage ); + +/* True if -hushasserts was passed on command line. */ +DBG_INTERFACE bool HushAsserts(); + +#if defined( USE_SDL ) +DBG_INTERFACE void SetAssertDialogParent( struct SDL_Window *window ); +DBG_INTERFACE struct SDL_Window * GetAssertDialogParent(); +#endif + +/* Used to define macros, never use these directly. */ + +#ifdef _PREFAST_ + // When doing /analyze builds define _AssertMsg to be __analysis_assume. This tells + // the compiler to assume that the condition is true, which helps to suppress many + // warnings. This define is done in debug and release builds. + // The unfortunate !! is necessary because otherwise /analyze is incapable of evaluating + // all of the logical expressions that the regular compiler can handle. + // Include _msg in the macro so that format errors in it are detected. + #define _AssertMsg( _exp, _msg, _executeExp, _bFatal ) do { __analysis_assume( !!(_exp) ); _msg; } while (0) + #define _AssertMsgOnce( _exp, _msg, _bFatal ) do { __analysis_assume( !!(_exp) ); _msg; } while (0) + // Force asserts on for /analyze so that we get a __analysis_assume of all of the constraints. + #define DBGFLAG_ASSERT + #define DBGFLAG_ASSERTFATAL + #define DBGFLAG_ASSERTDEBUG +#else + #define _AssertMsg( _exp, _msg, _executeExp, _bFatal ) \ + do { \ + if (!(_exp)) \ + { \ + _SpewInfo( SPEW_ASSERT, __TFILE__, __LINE__ ); \ + SpewRetval_t retAssert = _SpewMessage("%s", static_cast<const char*>( _msg )); \ + CallAssertFailedNotifyFunc( __TFILE__, __LINE__, _msg ); \ + _executeExp; \ + if ( retAssert == SPEW_DEBUGGER) \ + { \ + if ( !ShouldUseNewAssertDialog() || DoNewAssertDialog( __TFILE__, __LINE__, _msg ) ) \ + { \ + DebuggerBreak(); \ + } \ + if ( _bFatal ) \ + { \ + _ExitOnFatalAssert( __TFILE__, __LINE__ ); \ + } \ + } \ + } \ + } while (0) + + #define _AssertMsgOnce( _exp, _msg, _bFatal ) \ + do { \ + static bool fAsserted; \ + if (!fAsserted ) \ + { \ + _AssertMsg( _exp, _msg, (fAsserted = true), _bFatal ); \ + } \ + } while (0) +#endif + +/* Spew macros... */ + +// AssertFatal macros +// AssertFatal is used to detect an unrecoverable error condition. +// If enabled, it may display an assert dialog (if DBGFLAG_ASSERTDLG is turned on or running under the debugger), +// and always terminates the application + +#ifdef DBGFLAG_ASSERTFATAL + +#define AssertFatal( _exp ) _AssertMsg( _exp, _T("Assertion Failed: ") _T(#_exp), ((void)0), true ) +#define AssertFatalOnce( _exp ) _AssertMsgOnce( _exp, _T("Assertion Failed: ") _T(#_exp), true ) +#define AssertFatalMsg( _exp, _msg, ... ) _AssertMsg( _exp, (const tchar *)CDbgFmtMsg( _msg, ##__VA_ARGS__ ), ((void)0), true ) +#define AssertFatalMsgOnce( _exp, _msg ) _AssertMsgOnce( _exp, _msg, true ) +#define AssertFatalFunc( _exp, _f ) _AssertMsg( _exp, _T("Assertion Failed: " _T(#_exp), _f, true ) +#define AssertFatalEquals( _exp, _expectedValue ) AssertFatalMsg2( (_exp) == (_expectedValue), _T("Expected %d but got %d!"), (_expectedValue), (_exp) ) +#define AssertFatalFloatEquals( _exp, _expectedValue, _tol ) AssertFatalMsg2( fabs((_exp) - (_expectedValue)) <= (_tol), _T("Expected %f but got %f!"), (_expectedValue), (_exp) ) +#define VerifyFatal( _exp ) AssertFatal( _exp ) +#define VerifyEqualsFatal( _exp, _expectedValue ) AssertFatalEquals( _exp, _expectedValue ) + +#define AssertFatalMsg1( _exp, _msg, a1 ) AssertFatalMsg( _exp, _msg, a1 ) +#define AssertFatalMsg2( _exp, _msg, a1, a2 ) AssertFatalMsg( _exp, _msg, a1, a2 ) +#define AssertFatalMsg3( _exp, _msg, a1, a2, a3 ) AssertFatalMsg( _exp, _msg, a1, a2, a3 ) +#define AssertFatalMsg4( _exp, _msg, a1, a2, a3, a4 ) AssertFatalMsg( _exp, _msg, a1, a2, a3, a4 ) +#define AssertFatalMsg5( _exp, _msg, a1, a2, a3, a4, a5 ) AssertFatalMsg( _exp, _msg, a1, a2, a3, a4, a5 ) +#define AssertFatalMsg6( _exp, _msg, a1, a2, a3, a4, a5, a6 ) AssertFatalMsg( _exp, _msg, a1, a2, a3, a4, a5, a6 ) +#define AssertFatalMsg7( _exp, _msg, a1, a2, a3, a4, a5, a6, a7 ) AssertFatalMsg( _exp, _msg, a1, a2, a3, a4, a5, a6, a7 ) +#define AssertFatalMsg8( _exp, _msg, a1, a2, a3, a4, a5, a6, a7, a8 ) AssertFatalMsg( _exp, _msg, a1, a2, a3, a4, a5, a6, a7, a8 ) +#define AssertFatalMsg9( _exp, _msg, a1, a2, a3, a4, a5, a6, a7, a8, a9 ) AssertFatalMsg( _exp, _msg, a1, a2, a3, a4, a5, a6, a7, a8, a9 ) + +#else // DBGFLAG_ASSERTFATAL + +#define AssertFatal( _exp ) ((void)0) +#define AssertFatalOnce( _exp ) ((void)0) +#define AssertFatalMsg( _exp, _msg ) ((void)0) +#define AssertFatalMsgOnce( _exp, _msg ) ((void)0) +#define AssertFatalFunc( _exp, _f ) ((void)0) +#define AssertFatalEquals( _exp, _expectedValue ) ((void)0) +#define AssertFatalFloatEquals( _exp, _expectedValue, _tol ) ((void)0) +#define VerifyFatal( _exp ) (_exp) +#define VerifyEqualsFatal( _exp, _expectedValue ) (_exp) + +#define AssertFatalMsg1( _exp, _msg, a1 ) ((void)0) +#define AssertFatalMsg2( _exp, _msg, a1, a2 ) ((void)0) +#define AssertFatalMsg3( _exp, _msg, a1, a2, a3 ) ((void)0) +#define AssertFatalMsg4( _exp, _msg, a1, a2, a3, a4 ) ((void)0) +#define AssertFatalMsg5( _exp, _msg, a1, a2, a3, a4, a5 ) ((void)0) +#define AssertFatalMsg6( _exp, _msg, a1, a2, a3, a4, a5, a6 ) ((void)0) +#define AssertFatalMsg7( _exp, _msg, a1, a2, a3, a4, a5, a6, a7 ) ((void)0) +#define AssertFatalMsg8( _exp, _msg, a1, a2, a3, a4, a5, a6, a7, a8 ) ((void)0) +#define AssertFatalMsg9( _exp, _msg, a1, a2, a3, a4, a5, a6, a7, a8, a9 ) ((void)0) + +#endif // DBGFLAG_ASSERTFATAL + +// Assert macros +// Assert is used to detect an important but survivable error. +// It's only turned on when DBGFLAG_ASSERT is true. + +#ifdef DBGFLAG_ASSERT + +#define Assert( _exp ) _AssertMsg( _exp, _T("Assertion Failed: ") _T(#_exp), ((void)0), false ) +#define AssertMsg( _exp, _msg, ... ) _AssertMsg( _exp, (const tchar *)CDbgFmtMsg( _msg, ##__VA_ARGS__ ), ((void)0), false ) +#define AssertOnce( _exp ) _AssertMsgOnce( _exp, _T("Assertion Failed: ") _T(#_exp), false ) +#define AssertMsgOnce( _exp, _msg ) _AssertMsgOnce( _exp, _msg, false ) +#define AssertFunc( _exp, _f ) _AssertMsg( _exp, _T("Assertion Failed: ") _T(#_exp), _f, false ) +#define AssertEquals( _exp, _expectedValue ) AssertMsg2( (_exp) == (_expectedValue), _T("Expected %d but got %d!"), (_expectedValue), (_exp) ) +#define AssertFloatEquals( _exp, _expectedValue, _tol ) AssertMsg2( fabs((_exp) - (_expectedValue)) <= (_tol), _T("Expected %f but got %f!"), (_expectedValue), (_exp) ) +#define Verify( _exp ) Assert( _exp ) +#define VerifyMsg1( _exp, _msg, a1 ) AssertMsg1( _exp, _msg, a1 ) +#define VerifyMsg2( _exp, _msg, a1, a2 ) AssertMsg2( _exp, _msg, a1, a2 ) +#define VerifyMsg3( _exp, _msg, a1, a2, a3 ) AssertMsg3( _exp, _msg, a1, a2, a3 ) +#define VerifyEquals( _exp, _expectedValue ) AssertEquals( _exp, _expectedValue ) +#define DbgVerify( _exp ) Assert( _exp ) + +#define AssertMsg1( _exp, _msg, a1 ) AssertMsg( _exp, _msg, a1 ) +#define AssertMsg2( _exp, _msg, a1, a2 ) AssertMsg( _exp, _msg, a1, a2 ) +#define AssertMsg3( _exp, _msg, a1, a2, a3 ) AssertMsg( _exp, _msg, a1, a2, a3 ) +#define AssertMsg4( _exp, _msg, a1, a2, a3, a4 ) AssertMsg( _exp, _msg, a1, a2, a3, a4 ) +#define AssertMsg5( _exp, _msg, a1, a2, a3, a4, a5 ) AssertMsg( _exp, _msg, a1, a2, a3, a4, a5 ) +#define AssertMsg6( _exp, _msg, a1, a2, a3, a4, a5, a6 ) AssertMsg( _exp, _msg, a1, a2, a3, a4, a5, a6 ) +#define AssertMsg7( _exp, _msg, a1, a2, a3, a4, a5, a6, a7 ) AssertMsg( _exp, _msg, a1, a2, a3, a4, a5, a6, a7 ) +#define AssertMsg8( _exp, _msg, a1, a2, a3, a4, a5, a6, a7, a8 ) AssertMsg( _exp, _msg, a1, a2, a3, a4, a5, a6, a7, a8 ) +#define AssertMsg9( _exp, _msg, a1, a2, a3, a4, a5, a6, a7, a8, a9 ) AssertMsg( _exp, _msg, a1, a2, a3, a4, a5, a6, a7, a8, a9 ) + +#else // DBGFLAG_ASSERT + +#define Assert( _exp ) ((void)0) +#define AssertOnce( _exp ) ((void)0) +#define AssertMsg( _exp, _msg, ... ) ((void)0) +#define AssertMsgOnce( _exp, _msg ) ((void)0) +#define AssertFunc( _exp, _f ) ((void)0) +#define AssertEquals( _exp, _expectedValue ) ((void)0) +#define AssertFloatEquals( _exp, _expectedValue, _tol ) ((void)0) +#define Verify( _exp ) (_exp) +#define VerifyMsg1( _exp, _msg, a1 ) (_exp) +#define VerifyMsg2( _exp, _msg, a1, a2 ) (_exp) +#define VerifyMsg3( _exp, _msg, a1, a2, a3 ) (_exp) +#define VerifyEquals( _exp, _expectedValue ) (_exp) +#define DbgVerify( _exp ) (_exp) + +#define AssertMsg1( _exp, _msg, a1 ) ((void)0) +#define AssertMsg2( _exp, _msg, a1, a2 ) ((void)0) +#define AssertMsg3( _exp, _msg, a1, a2, a3 ) ((void)0) +#define AssertMsg4( _exp, _msg, a1, a2, a3, a4 ) ((void)0) +#define AssertMsg5( _exp, _msg, a1, a2, a3, a4, a5 ) ((void)0) +#define AssertMsg6( _exp, _msg, a1, a2, a3, a4, a5, a6 ) ((void)0) +#define AssertMsg6( _exp, _msg, a1, a2, a3, a4, a5, a6 ) ((void)0) +#define AssertMsg7( _exp, _msg, a1, a2, a3, a4, a5, a6, a7 ) ((void)0) +#define AssertMsg8( _exp, _msg, a1, a2, a3, a4, a5, a6, a7, a8 ) ((void)0) +#define AssertMsg9( _exp, _msg, a1, a2, a3, a4, a5, a6, a7, a8, a9 ) ((void)0) + +#endif // DBGFLAG_ASSERT + +// The Always version of the assert macros are defined even when DBGFLAG_ASSERT is not, +// so they will be available even in release. +#define AssertAlways( _exp ) _AssertMsg( _exp, _T("Assertion Failed: ") _T(#_exp), ((void)0), false ) +#define AssertMsgAlways( _exp, _msg ) _AssertMsg( _exp, _msg, ((void)0), false ) + +// Stringify a number +#define V_STRINGIFY_INTERNAL(x) #x +// Extra level of indirection needed when passing in a macro to avoid getting the macro name instead of value +#define V_STRINGIFY(x) V_STRINGIFY_INTERNAL(x) + +// Macros to help decorate warnings or errors with the location in code +#define FILE_LINE_FUNCTION_STRING __FILE__ "(" V_STRINGIFY(__LINE__) "):" __FUNCTION__ ":" +#define FILE_LINE_STRING __FILE__ "(" V_STRINGIFY(__LINE__) "):" +#define FUNCTION_LINE_STRING __FUNCTION__ "(" V_STRINGIFY(__LINE__) "): " + +// Handy define for inserting clickable messages into the build output. +// Use like this: +// #pragma MESSAGE("Some message") +#define MESSAGE(msg) message(__FILE__ "(" V_STRINGIFY(__LINE__) "): " msg) + + +#if !defined( _X360 ) || !defined( _RETAIL ) + +/* These are always compiled in */ +DBG_INTERFACE void Msg( PRINTF_FORMAT_STRING const tchar* pMsg, ... ) FMTFUNCTION( 1, 2 ); +DBG_INTERFACE void DMsg( const tchar *pGroupName, int level, PRINTF_FORMAT_STRING const tchar *pMsg, ... ) FMTFUNCTION( 3, 4 ); +DBG_INTERFACE void MsgV( PRINTF_FORMAT_STRING const tchar *pMsg, va_list arglist ); + +DBG_INTERFACE void Warning( PRINTF_FORMAT_STRING const tchar *pMsg, ... ) FMTFUNCTION( 1, 2 ); +DBG_INTERFACE void DWarning( const tchar *pGroupName, int level, PRINTF_FORMAT_STRING const tchar *pMsg, ... ) FMTFUNCTION( 3, 4 ); +DBG_INTERFACE void WarningV( PRINTF_FORMAT_STRING const tchar *pMsg, va_list arglist ); + +DBG_INTERFACE void Log( PRINTF_FORMAT_STRING const tchar *pMsg, ... ) FMTFUNCTION( 1, 2 ); +DBG_INTERFACE void DLog( const tchar *pGroupName, int level, PRINTF_FORMAT_STRING const tchar *pMsg, ... ) FMTFUNCTION( 3, 4 ); +DBG_INTERFACE void LogV( PRINTF_FORMAT_STRING const tchar *pMsg, va_list arglist ); + +#ifdef Error +// p4.cpp does a #define Error Warning and in that case the Error prototype needs to +// be consistent with the Warning prototype. +DBG_INTERFACE void Error( PRINTF_FORMAT_STRING const tchar *pMsg, ... ) FMTFUNCTION( 1, 2 ); +#else +DBG_INTERFACE void NORETURN Error( PRINTF_FORMAT_STRING const tchar *pMsg, ... ) FMTFUNCTION( 1, 2 ); +DBG_INTERFACE void NORETURN ErrorV( PRINTF_FORMAT_STRING const tchar *pMsg, va_list arglist ); + +#endif + +#else + +inline void Msg( ... ) {} +inline void DMsg( ... ) {} +inline void MsgV( PRINTF_FORMAT_STRING const tchar *pMsg, va_list arglist ) {} +inline void Warning( PRINTF_FORMAT_STRING const tchar *pMsg, ... ) {} +inline void WarningV( PRINTF_FORMAT_STRING const tchar *pMsg, va_list arglist ) {} +inline void DWarning( ... ) {} +inline void Log( ... ) {} +inline void DLog( ... ) {} +inline void LogV( PRINTF_FORMAT_STRING const tchar *pMsg, va_list arglist ) {} +inline void Error( ... ) {} +inline void ErrorV( PRINTF_FORMAT_STRING const tchar *pMsg, va_list arglist ) {} + +#endif + +// You can use this macro like a runtime assert macro. +// If the condition fails, then Error is called with the message. This macro is called +// like AssertMsg, where msg must be enclosed in parenthesis: +// +// ErrorIfNot( bCondition, ("a b c %d %d %d", 1, 2, 3) ); +#define ErrorIfNot( condition, msg ) \ + if ( condition ) \ + ; \ + else \ + { \ + Error msg; \ + } + +#if !defined( _X360 ) || !defined( _RETAIL ) + +/* A couple of super-common dynamic spew messages, here for convenience */ +/* These looked at the "developer" group */ +DBG_INTERFACE void DevMsg( int level, PRINTF_FORMAT_STRING const tchar* pMsg, ... ) FMTFUNCTION( 2, 3 ); +DBG_INTERFACE void DevWarning( int level, PRINTF_FORMAT_STRING const tchar *pMsg, ... ) FMTFUNCTION( 2, 3 ); +DBG_INTERFACE void DevLog( int level, PRINTF_FORMAT_STRING const tchar *pMsg, ... ) FMTFUNCTION( 2, 3 ); + +/* default level versions (level 1) */ +DBG_OVERLOAD void DevMsg( PRINTF_FORMAT_STRING const tchar* pMsg, ... ) FMTFUNCTION( 1, 2 ); +DBG_OVERLOAD void DevWarning( PRINTF_FORMAT_STRING const tchar *pMsg, ... ) FMTFUNCTION( 1, 2 ); +DBG_OVERLOAD void DevLog( PRINTF_FORMAT_STRING const tchar *pMsg, ... ) FMTFUNCTION( 1, 2 ); + +/* These looked at the "console" group */ +DBG_INTERFACE void ConColorMsg( int level, const Color& clr, PRINTF_FORMAT_STRING const tchar* pMsg, ... ) FMTFUNCTION( 3, 4 ); +DBG_INTERFACE void ConMsg( int level, PRINTF_FORMAT_STRING const tchar* pMsg, ... ) FMTFUNCTION( 2, 3 ); +DBG_INTERFACE void ConWarning( int level, PRINTF_FORMAT_STRING const tchar *pMsg, ... ) FMTFUNCTION( 2, 3 ); +DBG_INTERFACE void ConLog( int level, PRINTF_FORMAT_STRING const tchar *pMsg, ... ) FMTFUNCTION( 2, 3 ); + +/* default console version (level 1) */ +DBG_OVERLOAD void ConColorMsg( const Color& clr, PRINTF_FORMAT_STRING const tchar* pMsg, ... ) FMTFUNCTION( 2, 3 ); +DBG_OVERLOAD void ConMsg( PRINTF_FORMAT_STRING const tchar* pMsg, ... ) FMTFUNCTION( 1, 2 ); +DBG_OVERLOAD void ConWarning( PRINTF_FORMAT_STRING const tchar *pMsg, ... ) FMTFUNCTION( 1, 2 ); +DBG_OVERLOAD void ConLog( PRINTF_FORMAT_STRING const tchar *pMsg, ... ) FMTFUNCTION( 1, 2 ); + +/* developer console version (level 2) */ +DBG_INTERFACE void ConDColorMsg( const Color& clr, PRINTF_FORMAT_STRING const tchar* pMsg, ... ) FMTFUNCTION( 2, 3 ); +DBG_INTERFACE void ConDMsg( PRINTF_FORMAT_STRING const tchar* pMsg, ... ) FMTFUNCTION( 1, 2 ); +DBG_INTERFACE void ConDWarning( PRINTF_FORMAT_STRING const tchar *pMsg, ... ) FMTFUNCTION( 1, 2 ); +DBG_INTERFACE void ConDLog( PRINTF_FORMAT_STRING const tchar *pMsg, ... ) FMTFUNCTION( 1, 2 ); + +/* These looked at the "network" group */ +DBG_INTERFACE void NetMsg( int level, PRINTF_FORMAT_STRING const tchar* pMsg, ... ) FMTFUNCTION( 2, 3 ); +DBG_INTERFACE void NetWarning( int level, PRINTF_FORMAT_STRING const tchar *pMsg, ... ) FMTFUNCTION( 2, 3 ); +DBG_INTERFACE void NetLog( int level, PRINTF_FORMAT_STRING const tchar *pMsg, ... ) FMTFUNCTION( 2, 3 ); + +void ValidateSpew( class CValidator &validator ); + +#else + +inline void DevMsg( ... ) {} +inline void DevWarning( ... ) {} +inline void DevLog( ... ) {} +inline void ConMsg( ... ) {} +inline void ConLog( ... ) {} +inline void NetMsg( ... ) {} +inline void NetWarning( ... ) {} +inline void NetLog( ... ) {} + +#endif + +DBG_INTERFACE void COM_TimestampedLog( PRINTF_FORMAT_STRING char const *fmt, ... ) FMTFUNCTION( 1, 2 ); + +/* Code macros, debugger interface */ + +#ifdef DBGFLAG_ASSERT + +#define DBG_CODE( _code ) if (0) ; else { _code } +#define DBG_CODE_NOSCOPE( _code ) _code +#define DBG_DCODE( _g, _l, _code ) if (IsSpewActive( _g, _l )) { _code } else {} +#define DBG_BREAK() DebuggerBreak() /* defined in platform.h */ + +#else /* not _DEBUG */ + +#define DBG_CODE( _code ) ((void)0) +#define DBG_CODE_NOSCOPE( _code ) +#define DBG_DCODE( _g, _l, _code ) ((void)0) +#define DBG_BREAK() ((void)0) + +#endif /* _DEBUG */ + +//----------------------------------------------------------------------------- + +#ifndef _RETAIL +class CScopeMsg +{ +public: + CScopeMsg( const char *pszScope ) + { + m_pszScope = pszScope; + Msg( "%s { ", pszScope ); + } + ~CScopeMsg() + { + Msg( "} %s", m_pszScope ); + } + const char *m_pszScope; +}; +#define SCOPE_MSG( msg ) CScopeMsg scopeMsg( msg ) +#else +#define SCOPE_MSG( msg ) +#endif + + +//----------------------------------------------------------------------------- +// This macro predates universal static_assert support in our toolchains +#define COMPILE_TIME_ASSERT( pred ) static_assert( pred, "Compile time assert constraint is not true: " #pred ) + +// ASSERT_INVARIANT used to be needed in order to allow COMPILE_TIME_ASSERTs at global +// scope. However the new COMPILE_TIME_ASSERT macro supports that by default. +#define ASSERT_INVARIANT( pred ) COMPILE_TIME_ASSERT( pred ) + +// NOTE: On GCC / Clang, assert_cast can sometimes fire even if the type is correct. We should just workaround these. +// The situation where this would occur is +// 1. You create an object of a low level type in a DLL, and it really gets created there. +// 2. You pass it across a DLL boundary +// 3. You use assert_cast to verify it in the second DLL boundary (where it also could've been created). +#ifdef _DEBUG +template<typename DEST_POINTER_TYPE, typename SOURCE_POINTER_TYPE> +inline DEST_POINTER_TYPE assert_cast(SOURCE_POINTER_TYPE* pSource) +{ + Assert( static_cast<DEST_POINTER_TYPE>(pSource) == dynamic_cast<DEST_POINTER_TYPE>(pSource) ); + return static_cast<DEST_POINTER_TYPE>(pSource); +} +#else +#define assert_cast static_cast +#endif + +//----------------------------------------------------------------------------- +// Templates to assist in validating pointers: + +// Have to use these stubs so we don't have to include windows.h here. + +DBG_INTERFACE void _AssertValidReadPtr( void* ptr, int count = 1 ); +DBG_INTERFACE void _AssertValidWritePtr( void* ptr, int count = 1 ); +DBG_INTERFACE void _AssertValidReadWritePtr( void* ptr, int count = 1 ); +DBG_INTERFACE void AssertValidStringPtr( const tchar* ptr, int maxchar = 0xFFFFFF ); + +#ifdef DBGFLAG_ASSERT + +FORCEINLINE void AssertValidReadPtr( const void* ptr, int count = 1 ) { _AssertValidReadPtr( (void*)ptr, count ); } +FORCEINLINE void AssertValidWritePtr( const void* ptr, int count = 1 ) { _AssertValidWritePtr( (void*)ptr, count ); } +FORCEINLINE void AssertValidReadWritePtr( const void* ptr, int count = 1 ) { _AssertValidReadWritePtr( (void*)ptr, count ); } + +#else + +FORCEINLINE void AssertValidReadPtr( const void* ptr, int count = 1 ) { } +FORCEINLINE void AssertValidWritePtr( const void* ptr, int count = 1 ) { } +FORCEINLINE void AssertValidReadWritePtr( const void* ptr, int count = 1 ) { } +#define AssertValidStringPtr AssertValidReadPtr + +#endif + +#define AssertValidThis() AssertValidReadWritePtr(this,sizeof(*this)) + +//----------------------------------------------------------------------------- +// Macro to protect functions that are not reentrant + +#ifdef _DEBUG +class CReentryGuard +{ +public: + CReentryGuard(int *pSemaphore) + : m_pSemaphore(pSemaphore) + { + ++(*m_pSemaphore); + } + + ~CReentryGuard() + { + --(*m_pSemaphore); + } + +private: + int *m_pSemaphore; +}; + +#define ASSERT_NO_REENTRY() \ + static int fSemaphore##__LINE__; \ + Assert( !fSemaphore##__LINE__ ); \ + CReentryGuard ReentryGuard##__LINE__( &fSemaphore##__LINE__ ) +#else +#define ASSERT_NO_REENTRY() +#endif + +//----------------------------------------------------------------------------- +// +// Purpose: Inline string formatter +// + +#include "tier0/valve_off.h" +class CDbgFmtMsg +{ +public: + CDbgFmtMsg(PRINTF_FORMAT_STRING const tchar *pszFormat, ...) FMTFUNCTION( 2, 3 ) + { + va_list arg_ptr; + + va_start(arg_ptr, pszFormat); + _vsntprintf(m_szBuf, sizeof(m_szBuf)-1, pszFormat, arg_ptr); + va_end(arg_ptr); + + m_szBuf[sizeof(m_szBuf)-1] = 0; + } + + operator const tchar *() const + { + return m_szBuf; + } + +private: + tchar m_szBuf[256]; +}; +#include "tier0/valve_on.h" + +//----------------------------------------------------------------------------- +// +// Purpose: Embed debug info in each file. +// +#if defined( _WIN32 ) && !defined( _X360 ) + + #ifdef _DEBUG + #pragma comment(compiler) + #endif + +#endif + +//----------------------------------------------------------------------------- +// +// Purpose: Wrap around a variable to create a simple place to put a breakpoint +// + +#ifdef _DEBUG + +template< class Type > +class CDataWatcher +{ +public: + const Type& operator=( const Type &val ) + { + return Set( val ); + } + + const Type& operator=( const CDataWatcher<Type> &val ) + { + return Set( val.m_Value ); + } + + const Type& Set( const Type &val ) + { + // Put your breakpoint here + m_Value = val; + return m_Value; + } + + Type& GetForModify() + { + return m_Value; + } + + const Type& operator+=( const Type &val ) + { + return Set( m_Value + val ); + } + + const Type& operator-=( const Type &val ) + { + return Set( m_Value - val ); + } + + const Type& operator/=( const Type &val ) + { + return Set( m_Value / val ); + } + + const Type& operator*=( const Type &val ) + { + return Set( m_Value * val ); + } + + const Type& operator^=( const Type &val ) + { + return Set( m_Value ^ val ); + } + + const Type& operator|=( const Type &val ) + { + return Set( m_Value | val ); + } + + const Type& operator++() + { + return (*this += 1); + } + + Type operator--() + { + return (*this -= 1); + } + + Type operator++( int ) // postfix version.. + { + Type val = m_Value; + (*this += 1); + return val; + } + + Type operator--( int ) // postfix version.. + { + Type val = m_Value; + (*this -= 1); + return val; + } + + // For some reason the compiler only generates type conversion warnings for this operator when used like + // CNetworkVarBase<unsigned tchar> = 0x1 + // (it warns about converting from an int to an unsigned char). + template< class C > + const Type& operator&=( C val ) + { + return Set( m_Value & val ); + } + + operator const Type&() const + { + return m_Value; + } + + const Type& Get() const + { + return m_Value; + } + + const Type* operator->() const + { + return &m_Value; + } + + Type m_Value; + +}; + +#else + +template< class Type > +class CDataWatcher +{ +private: + CDataWatcher(); // refuse to compile in non-debug builds +}; + +#endif + +//----------------------------------------------------------------------------- + + +// This is horrible, but we don't want to integrate CS:GO new logging system atm. +#define Log_Warning( ignore, ... ) ::Msg( __VA_ARGS__ ); ::Log( __VA_ARGS__ ); +#define Log_Msg( ignore, ... ) ::Warning( __VA_ARGS__ ); ::Log( __VA_ARGS__ ); +#define Log_Error( ignore, ... ) ::Error( __VA_ARGS__ ); +#define DEFINE_LOGGING_CHANNEL_NO_TAGS( ... ); +#define Plat_FatalError( ... ) do { Log_Error( LOG_GENERAL, __VA_ARGS__ ); Plat_ExitProcess( EXIT_FAILURE ); } while( 0 ) + +#endif /* DBG_H */ diff --git a/public/tier0/dbgflag.h b/public/tier0/dbgflag.h new file mode 100644 index 0000000..05f84c9 --- /dev/null +++ b/public/tier0/dbgflag.h @@ -0,0 +1,65 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: This file sets all of our debugging flags. It should be +// called before all other header files. +// +// $NoKeywords: $ +//=============================================================================// + +#ifndef DBGFLAG_H +#define DBGFLAG_H +#ifdef _WIN32 +#pragma once +#endif + + +// Here are all the flags we support: +// DBGFLAG_MEMORY: Enables our memory debugging system, which overrides malloc & free +// DBGFLAG_MEMORY_NEWDEL: Enables new / delete tracking for memory debug system. Requires DBGFLAG_MEMORY to be enabled. +// DBGFLAG_VALIDATE: Enables our recursive validation system for checking integrity and memory leaks +// DBGFLAG_ASSERT: Turns Assert on or off (when off, it isn't compiled at all) +// DBGFLAG_ASSERTFATAL: Turns AssertFatal on or off (when off, it isn't compiled at all) +// DBGFLAG_ASSERTDLG: Turns assert dialogs on or off and debug breaks on or off when not under the debugger. +// (Dialogs will always be on when process is being debugged.) +// DBGFLAG_STRINGS: Turns on hardcore string validation (slow but safe) + +#undef DBGFLAG_MEMORY +#undef DBGFLAG_MEMORY_NEWDEL +#undef DBGFLAG_VALIDATE +#undef DBGFLAG_ASSERT +#undef DBGFLAG_ASSERTFATAL +#undef DBGFLAG_ASSERTDLG +#undef DBGFLAG_STRINGS + +//----------------------------------------------------------------------------- +// Default flags for debug builds +//----------------------------------------------------------------------------- +#if (defined( _DEBUG ) || defined( RELEASEASSERTS ) ) + +#define DBGFLAG_MEMORY +#ifdef _SERVER // only enable new & delete tracking for server; on client it conflicts with CRT mem leak tracking +#define DBGFLAG_MEMORY_NEWDEL +#endif +#ifdef STEAM +#define DBGFLAG_VALIDATE +#endif +#define DBGFLAG_ASSERT +#define DBGFLAG_ASSERTFATAL +#define DBGFLAG_ASSERTDLG +#define DBGFLAG_STRINGS + + +//----------------------------------------------------------------------------- +// Default flags for release builds +//----------------------------------------------------------------------------- +#else // _DEBUG + +#ifdef STEAM +#define DBGFLAG_ASSERT +#endif +#define DBGFLAG_ASSERTFATAL // note: fatal asserts are enabled in release builds +#define DBGFLAG_ASSERTDLG + +#endif // _DEBUG + +#endif // DBGFLAG_H diff --git a/public/tier0/dynfunction.h b/public/tier0/dynfunction.h new file mode 100644 index 0000000..bd976b7 --- /dev/null +++ b/public/tier0/dynfunction.h @@ -0,0 +1,136 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +// +//=============================================================================// + +// This makes it easy to dynamically load a shared library and lookup a +// function in that library. +// +// Usage: +// CDynamicFunction<void (*)(const char *)> MyPuts(libname, "puts"); +// if (MyPuts) +// MyPuts("Hello world!"); +// +// Please note that this interface does not distinguish between functions and +// data. If you look up a global variable in your shared library, or simply +// mess up the function signature, you'll get a valid pointer and a crash +// if you call it as a function. + +#ifndef DYNFUNCTION_H +#define DYNFUNCTION_H +#pragma once + +#include "tier0/platform.h" + +// The heavy lifting isn't template-specific, so we move it out of the header. +DLL_EXPORT void *VoidFnPtrLookup_Tier0(const char *libname, const char *fn, void *fallback); + +template < class FunctionType > +class CDynamicFunction +{ +public: + // Construct with a NULL function pointer. You must manually call + // Lookup() before you can call a dynamic function through this interface. + CDynamicFunction() : m_pFn(NULL) {} + + // Construct and do a lookup right away. You will need to make sure that + // the lookup actually succeeded, as (libname) might have failed to load + // or (fn) might not exist in it. + CDynamicFunction(const char *libname, const char *fn, FunctionType fallback=NULL) : m_pFn(NULL) + { + Lookup(libname, fn, fallback); + } + + // Construct and do a lookup right away. See comments in Lookup() about what (okay) does. + CDynamicFunction(const char *libname, const char *fn, bool &okay, FunctionType fallback=NULL) : m_pFn(NULL) + { + Lookup(libname, fn, okay, fallback); + } + + // Load library if necessary, look up symbol. Returns true and sets + // m_pFn on successful lookup, returns false otherwise. If the + // function pointer is already looked up, this return true immediately. + // Use Reset() first if you want to look up the symbol again. + // This function will return false immediately unless (okay) is true. + // This allows you to chain lookups like this: + // bool okay = true; + // x.Lookup(lib, "x", okay); + // y.Lookup(lib, "y", okay); + // z.Lookup(lib, "z", okay); + // if (okay) { printf("All functions were loaded successfully!\n"); } + // If you supply a fallback, it'll be used if the lookup fails (and if + // non-NULL, means this will always return (okay)). + bool Lookup(const char *libname, const char *fn, bool &okay, FunctionType fallback=NULL) + { + if (!okay) + return false; + else if (m_pFn == NULL) + m_pFn = (FunctionType) VoidFnPtrLookup_Tier0(libname, fn, (void *) fallback); + okay = m_pFn != NULL; + return okay; + } + + // Load library if necessary, look up symbol. Returns true and sets + // m_pFn on successful lookup, returns false otherwise. If the + // function pointer is already looked up, this return true immediately. + // Use Reset() first if you want to look up the symbol again. + // This function will return false immediately unless (okay) is true. + // If you supply a fallback, it'll be used if the lookup fails (and if + // non-NULL, means this will always return true). + bool Lookup(const char *libname, const char *fn, FunctionType fallback=NULL) + { + bool okay = true; + return Lookup(libname, fn, okay, fallback); + } + + // Invalidates the current lookup. Makes the function pointer NULL. You + // will need to call Lookup() before you can call a dynamic function + // through this interface again. + void Reset() { m_pFn = NULL; } + + // Force this to be a specific function pointer. + void Force(FunctionType ptr) { m_pFn = ptr; } + + // Retrieve the actual function pointer. + FunctionType Pointer() const { return m_pFn; } + operator FunctionType() const { return m_pFn; } + + // Can be used to verify that we have an actual function looked up and + // ready to call: if (!MyDynFunc) { printf("Function not found!\n"); } + operator bool () const { return m_pFn != NULL; } + bool operator !() const { return m_pFn == NULL; } + +protected: + FunctionType m_pFn; +}; + + +// This is the same as CDynamicFunction, but we made the default constructor +// private, forcing you to do loading/lookup during construction. +// The usage pattern is to have a list of dynamic functions that are +// constructed en masse as part of another class's constructor, with the +// possibility of human error removed (the compiler will complain if you +// forget to initialize one). +template < class FunctionType > +class CDynamicFunctionMustInit : public CDynamicFunction < FunctionType > +{ +private: // forbid default constructor. + CDynamicFunctionMustInit() {} + +public: + CDynamicFunctionMustInit(const char *libname, const char *fn, FunctionType fallback=NULL) + : CDynamicFunction< FunctionType >(libname, fn, fallback) + { + } + + CDynamicFunctionMustInit(const char *libname, const char *fn, bool &okay, FunctionType fallback=NULL) + : CDynamicFunction< FunctionType >(libname, fn, okay, fallback) + { + } +}; + +#endif // DYNFUNCTION_H + diff --git a/public/tier0/etwprof.h b/public/tier0/etwprof.h new file mode 100644 index 0000000..c1d4282 --- /dev/null +++ b/public/tier0/etwprof.h @@ -0,0 +1,157 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// ETW (Event Tracing for Windows) profiling helpers. +// This allows easy insertion of Generic Event markers into ETW/xperf tracing +// which then aids in analyzing the traces and finding performance problems. +// The usage patterns are to use ETWBegin and ETWEnd (typically through the +// convenience class CETWScope) to bracket time-consuming operations. In addition +// ETWFrameMark marks the beginning of each frame, and ETWMark can be used to +// mark other notable events. More event types and providers can be added as needed. +// When recording xperf profiles add Valve-Main+Valve-FrameRate to the list of +// user-mode providers and be sure to register the providers with this sequence +// of commands: +// xcopy /y game\bin\tier0.dll %temp% +// wevtutil um src\tier0\ValveETWProvider.man +// wevtutil im src\tier0\ValveETWProvider.man +// +//=============================================================================== + +#ifndef ETWPROF_H +#define ETWPROF_H +#if defined( COMPILER_MSVC ) +#pragma once +#endif + +#include "tier0/platform.h" + +#ifdef IS_WINDOWS_PC +// ETW support should be compiled in for all Windows PC platforms. It isn't +// supported on Windows XP but that is determined at run-time. +#define ETW_MARKS_ENABLED +#endif + +#ifdef ETW_MARKS_ENABLED + +// Insert a single event to mark a point in an ETW trace. The return value is a 64-bit +// time stamp. +PLATFORM_INTERFACE int64 ETWMark( const char *pMessage ); +// Optionally do full printf formatting of the mark string. This will be more expensive, +// but only when tracing is enabled. +PLATFORM_INTERFACE void ETWMarkPrintf( PRINTF_FORMAT_STRING const char *pMessage, ... ) FMTFUNCTION( 1, 2 ); +// Optionally specify one to four floats. They will show up in separate columns in +// summary tables to allow sorting and easier transfer to spreadsheets. +PLATFORM_INTERFACE void ETWMark1F( const char *pMessage, float data1 ); +PLATFORM_INTERFACE void ETWMark2F( const char *pMessage, float data1, float data2 ); +PLATFORM_INTERFACE void ETWMark3F( const char *pMessage, float data1, float data2, float data3 ); +PLATFORM_INTERFACE void ETWMark4F( const char *pMessage, float data1, float data2, float data3, float data4 ); +// Optionally specify one to four ints. They will show up in separate columns in +// summary tables to allow sorting and easier transfer to spreadsheets. +PLATFORM_INTERFACE void ETWMark1I( const char *pMessage, int data1 ); +PLATFORM_INTERFACE void ETWMark2I( const char *pMessage, int data1, int data2 ); +PLATFORM_INTERFACE void ETWMark3I( const char *pMessage, int data1, int data2, int data3 ); +PLATFORM_INTERFACE void ETWMark4I( const char *pMessage, int data1, int data2, int data3, int data4 ); +// Optionally specify one to two strings. They will show up in separate columns in +// summary tables to allow sorting and easier transfer to spreadsheets. +PLATFORM_INTERFACE void ETWMark1S( const char *pMessage, const char* data1 ); +PLATFORM_INTERFACE void ETWMark2S( const char *pMessage, const char* data1, const char* data2 ); + +// Insert a begin event to mark the start of some work. The return value is a 64-bit +// time stamp which should be passed to the corresponding ETWEnd function. +PLATFORM_INTERFACE int64 ETWBegin( const char *pMessage ); + +// Insert a paired end event to mark the end of some work. +PLATFORM_INTERFACE int64 ETWEnd( const char *pMessage, int64 nStartTime ); + +// Mark the start of the next render frame. bIsServerProcess must be passed +// in consistently for a particular process. +PLATFORM_INTERFACE void ETWRenderFrameMark( bool bIsServerProcess ); +// Mark the start of the next simulation frame. bIsServerProcess must be passed +// in consistently for a particular process. +PLATFORM_INTERFACE void ETWSimFrameMark( bool bIsServerProcess ); +// Return the frame number recorded in the ETW trace -- useful for synchronizing +// other profile information to the ETW trace. +PLATFORM_INTERFACE int ETWGetRenderFrameNumber(); + +PLATFORM_INTERFACE void ETWMouseDown( int nWhichButton, int nX, int nY ); +PLATFORM_INTERFACE void ETWMouseUp( int nWhichButton, int nX, int nY ); +PLATFORM_INTERFACE void ETWMouseMove( int nX, int nY ); +PLATFORM_INTERFACE void ETWMouseWheel( int nWheelDelta, int nX, int nY ); +PLATFORM_INTERFACE void ETWKeyDown( int nScanCode, int nVirtualCode, const char *pChar ); + +PLATFORM_INTERFACE void ETWSendPacket( const char *pTo, int nWireSize, int nOutSequenceNR, int nOutSequenceNrAck ); +PLATFORM_INTERFACE void ETWThrottled(); +PLATFORM_INTERFACE void ETWReadPacket( const char *pFrom, int nWireSize, int nInSequenceNR, int nOutSequenceNRAck ); + +// This class calls the ETW Begin and End functions in order to insert a +// pair of events to bracket some work. +class CETWScope +{ +public: + CETWScope( const char *pMessage ) + : m_pMessage( pMessage ) + { + m_nStartTime = ETWBegin( pMessage ); + } + ~CETWScope() + { + ETWEnd( m_pMessage, m_nStartTime ); + } +private: + // Private and unimplemented to disable copying. + CETWScope( const CETWScope& rhs ); + CETWScope& operator=( const CETWScope& rhs ); + + const char* m_pMessage; + int64 m_nStartTime; +}; + +#else + +inline int64 ETWMark( const char* ) { return 0; } +inline void ETWMarkPrintf( const char *, ... ) { return; } +inline void ETWMark1F( const char *, float ) { } +inline void ETWMark2F( const char *, float , float ) { } +inline void ETWMark3F( const char *, float , float , float ) { } +inline void ETWMark4F( const char *, float , float , float , float ) { } +inline void ETWMark1I( const char *, int ) { } +inline void ETWMark2I( const char *, int , int ) { } +inline void ETWMark3I( const char *, int , int , int ) { } +inline void ETWMark4I( const char *, int , int , int , int ) { } +// Optionally specify one to two strings. They will show up in separate columns in +// summary tables to allow sorting and easier transfer to spreadsheets. +inline void ETWMark1S( const char *, const char* ) { } +inline void ETWMark2S( const char *, const char* , const char* ) { } + +inline int64 ETWBegin( const char* ) { return 0; } +inline int64 ETWEnd( const char*, int64 ) { return 0; } +inline void ETWRenderFrameMark( bool ) {} +inline void ETWSimFrameMark( bool ) {} +inline int ETWGetRenderFrameNumber() { return 0; } + +inline void ETWMouseDown( int nWhichButton, int nX, int nY ) {} +inline void ETWMouseUp( int nWhichButton, int nX, int nY ) {} +inline void ETWMouseMove( int nX, int nY ) {} +inline void ETWMouseWheel( int nWheelDelta, int nX, int nY ) {} +inline void ETWKeyDown( int nScanCode, int nVirtualCode, const char *pChar ) {} + +inline void ETWSendPacket( const char *pTo, int nWireSize, int nOutSequenceNR, int nOutSequenceNrAck ) {} +inline void ETWThrottled() {} +inline void ETWReadPacket( const char *pFrom, int nWireSize, int nInSequenceNR, int nOutSequenceNRAck ) {} + +// This class calls the ETW Begin and End functions in order to insert a +// pair of events to bracket some work. +class CETWScope +{ +public: + CETWScope( const char* ) + { + } +private: + // Private and unimplemented to disable copying. + CETWScope( const CETWScope& rhs ); + CETWScope& operator=( const CETWScope& rhs ); +}; + +#endif + +#endif // ETWPROF_H diff --git a/public/tier0/fasttimer.h b/public/tier0/fasttimer.h new file mode 100644 index 0000000..e9e634f --- /dev/null +++ b/public/tier0/fasttimer.h @@ -0,0 +1,569 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +//=============================================================================// + +#ifndef FASTTIMER_H +#define FASTTIMER_H +#ifdef _WIN32 +#pragma once +#endif + +#ifdef _WIN32 +#include <intrin.h> +#endif + +#include <assert.h> +#include "tier0/platform.h" + +PLATFORM_INTERFACE uint64 g_ClockSpeed; +#if defined( _X360 ) && defined( _CERT ) +PLATFORM_INTERFACE unsigned long g_dwFakeFastCounter; +#endif + +PLATFORM_INTERFACE double g_ClockSpeedMicrosecondsMultiplier; +PLATFORM_INTERFACE double g_ClockSpeedMillisecondsMultiplier; +PLATFORM_INTERFACE double g_ClockSpeedSecondsMultiplier; + +class CCycleCount +{ +friend class CFastTimer; + +public: + CCycleCount(); + CCycleCount( uint64 cycles ); + + void Sample(); // Sample the clock. This takes about 34 clocks to execute (or 26,000 calls per millisecond on a P900). + + void Init(); // Set to zero. + void Init( float initTimeMsec ); + void Init( double initTimeMsec ) { Init( (float)initTimeMsec ); } + void Init( uint64 cycles ); + bool IsLessThan( CCycleCount const &other ) const; // Compare two counts. + + // Convert to other time representations. These functions are slow, so it's preferable to call them + // during display rather than inside a timing block. + unsigned long GetCycles() const; + uint64 GetLongCycles() const; + + unsigned long GetMicroseconds() const; + uint64 GetUlMicroseconds() const; + double GetMicrosecondsF() const; + void SetMicroseconds( unsigned long nMicroseconds ); + + unsigned long GetMilliseconds() const; + double GetMillisecondsF() const; + + double GetSeconds() const; + + CCycleCount& operator+=( CCycleCount const &other ); + + // dest = rSrc1 + rSrc2 + static void Add( CCycleCount const &rSrc1, CCycleCount const &rSrc2, CCycleCount &dest ); // Add two samples together. + + // dest = rSrc1 - rSrc2 + static void Sub( CCycleCount const &rSrc1, CCycleCount const &rSrc2, CCycleCount &dest ); // Add two samples together. + + static uint64 GetTimestamp(); + + uint64 m_Int64; +}; + +class PLATFORM_CLASS CClockSpeedInit +{ +public: + CClockSpeedInit() + { + Init(); + } + + static void Init(); +}; + +class CFastTimer +{ +public: + // These functions are fast to call and should be called from your sampling code. + void Start(); + void End(); + + const CCycleCount & GetDuration() const; // Get the elapsed time between Start and End calls. + CCycleCount GetDurationInProgress() const; // Call without ending. Not that cheap. + + // Return number of cycles per second on this processor. + static inline int64 GetClockSpeed(); + +private: + CCycleCount m_Duration; +#ifdef DEBUG_FASTTIMER + bool m_bRunning; // Are we currently running? +#endif +}; + + +// This is a helper class that times whatever block of code it's in +class CTimeScope +{ +public: + CTimeScope( CFastTimer *pTimer ); + ~CTimeScope(); + +private: + CFastTimer *m_pTimer; +}; + +inline CTimeScope::CTimeScope( CFastTimer *pTotal ) +{ + m_pTimer = pTotal; + m_pTimer->Start(); +} + +inline CTimeScope::~CTimeScope() +{ + m_pTimer->End(); +} + +// This is a helper class that times whatever block of code it's in and +// adds the total (int microseconds) to a global counter. +class CTimeAdder +{ +public: + CTimeAdder( CCycleCount *pTotal ); + ~CTimeAdder(); + + void End(); + +private: + CCycleCount *m_pTotal; + CFastTimer m_Timer; +}; + +inline CTimeAdder::CTimeAdder( CCycleCount *pTotal ) +{ + m_pTotal = pTotal; + m_Timer.Start(); +} + +inline CTimeAdder::~CTimeAdder() +{ + End(); +} + +inline void CTimeAdder::End() +{ + if( m_pTotal ) + { + m_Timer.End(); + *m_pTotal += m_Timer.GetDuration(); + m_pTotal = 0; + } +} + + + +// -------------------------------------------------------------------------- // +// Simple tool to support timing a block of code, and reporting the results on +// program exit or at each iteration +// +// Macros used because dbg.h uses this header, thus Msg() is unavailable +// -------------------------------------------------------------------------- // + +#define PROFILE_SCOPE(name) \ + class C##name##ACC : public CAverageCycleCounter \ + { \ + public: \ + ~C##name##ACC() \ + { \ + Msg("%-48s: %6.3f avg (%8.1f total, %7.3f peak, %5d iters)\n", \ + #name, \ + GetAverageMilliseconds(), \ + GetTotalMilliseconds(), \ + GetPeakMilliseconds(), \ + GetIters() ); \ + } \ + }; \ + static C##name##ACC name##_ACC; \ + CAverageTimeMarker name##_ATM( &name##_ACC ) + +#define TIME_SCOPE(name) \ + class CTimeScopeMsg_##name \ + { \ + public: \ + CTimeScopeMsg_##name() { m_Timer.Start(); } \ + ~CTimeScopeMsg_##name() \ + { \ + m_Timer.End(); \ + Msg( #name "time: %.4fms\n", m_Timer.GetDuration().GetMillisecondsF() ); \ + } \ + private: \ + CFastTimer m_Timer; \ + } name##_TSM; + + +// -------------------------------------------------------------------------- // + +class CAverageCycleCounter +{ +public: + CAverageCycleCounter(); + + void Init(); + void MarkIter( const CCycleCount &duration ); + + unsigned GetIters() const; + + double GetAverageMilliseconds() const; + double GetTotalMilliseconds() const; + double GetPeakMilliseconds() const; + +private: + unsigned m_nIters; + CCycleCount m_Total; + CCycleCount m_Peak; +}; + +// -------------------------------------------------------------------------- // + +class CAverageTimeMarker +{ +public: + CAverageTimeMarker( CAverageCycleCounter *pCounter ); + ~CAverageTimeMarker(); + +private: + CAverageCycleCounter *m_pCounter; + CFastTimer m_Timer; +}; + + +// -------------------------------------------------------------------------- // +// CCycleCount inlines. +// -------------------------------------------------------------------------- // + +inline CCycleCount::CCycleCount() +{ + Init( (uint64)0 ); +} + +inline CCycleCount::CCycleCount( uint64 cycles ) +{ + Init( cycles ); +} + +inline void CCycleCount::Init() +{ + Init( (uint64)0 ); +} + +inline void CCycleCount::Init( float initTimeMsec ) +{ + if ( g_ClockSpeedMillisecondsMultiplier > 0 ) + Init( (uint64)(initTimeMsec / g_ClockSpeedMillisecondsMultiplier) ); + else + Init( (uint64)0 ); +} + +inline void CCycleCount::Init( uint64 cycles ) +{ + m_Int64 = cycles; +} + +inline void CCycleCount::Sample() +{ + m_Int64 = Plat_Rdtsc(); +} + +inline CCycleCount& CCycleCount::operator+=( CCycleCount const &other ) +{ + m_Int64 += other.m_Int64; + return *this; +} + + +inline void CCycleCount::Add( CCycleCount const &rSrc1, CCycleCount const &rSrc2, CCycleCount &dest ) +{ + dest.m_Int64 = rSrc1.m_Int64 + rSrc2.m_Int64; +} + +inline void CCycleCount::Sub( CCycleCount const &rSrc1, CCycleCount const &rSrc2, CCycleCount &dest ) +{ + dest.m_Int64 = rSrc1.m_Int64 - rSrc2.m_Int64; +} + +inline uint64 CCycleCount::GetTimestamp() +{ + CCycleCount c; + c.Sample(); + return c.GetLongCycles(); +} + +inline bool CCycleCount::IsLessThan(CCycleCount const &other) const +{ + return m_Int64 < other.m_Int64; +} + + +inline unsigned long CCycleCount::GetCycles() const +{ + return (unsigned long)m_Int64; +} + +inline uint64 CCycleCount::GetLongCycles() const +{ + return m_Int64; +} + +inline unsigned long CCycleCount::GetMicroseconds() const +{ + return (unsigned long)((m_Int64 * 1000000) / g_ClockSpeed); +} + +inline uint64 CCycleCount::GetUlMicroseconds() const +{ + return ((m_Int64 * 1000000) / g_ClockSpeed); +} + + +inline double CCycleCount::GetMicrosecondsF() const +{ + return (double)( m_Int64 * g_ClockSpeedMicrosecondsMultiplier ); +} + + +inline void CCycleCount::SetMicroseconds( unsigned long nMicroseconds ) +{ + m_Int64 = ((uint64)nMicroseconds * g_ClockSpeed) / 1000000; +} + + +inline unsigned long CCycleCount::GetMilliseconds() const +{ + return (unsigned long)((m_Int64 * 1000) / g_ClockSpeed); +} + + +inline double CCycleCount::GetMillisecondsF() const +{ + return (double)( m_Int64 * g_ClockSpeedMillisecondsMultiplier ); +} + + +inline double CCycleCount::GetSeconds() const +{ + return (double)( m_Int64 * g_ClockSpeedSecondsMultiplier ); +} + + +// -------------------------------------------------------------------------- // +// CFastTimer inlines. +// -------------------------------------------------------------------------- // +inline void CFastTimer::Start() +{ + m_Duration.Sample(); +#ifdef DEBUG_FASTTIMER + m_bRunning = true; +#endif +} + + +inline void CFastTimer::End() +{ + CCycleCount cnt; + cnt.Sample(); + if ( IsX360() ) + { + // have to handle rollover, hires timer is only accurate to 32 bits + // more than one overflow should not have occurred, otherwise caller should use a slower timer + if ( (uint64)cnt.m_Int64 <= (uint64)m_Duration.m_Int64 ) + { + // rollover occurred + cnt.m_Int64 += 0x100000000LL; + } + } + + m_Duration.m_Int64 = cnt.m_Int64 - m_Duration.m_Int64; + +#ifdef DEBUG_FASTTIMER + m_bRunning = false; +#endif +} + +inline CCycleCount CFastTimer::GetDurationInProgress() const +{ + CCycleCount cnt; + cnt.Sample(); + if ( IsX360() ) + { + // have to handle rollover, hires timer is only accurate to 32 bits + // more than one overflow should not have occurred, otherwise caller should use a slower timer + if ( (uint64)cnt.m_Int64 <= (uint64)m_Duration.m_Int64 ) + { + // rollover occurred + cnt.m_Int64 += 0x100000000LL; + } + } + + CCycleCount result; + result.m_Int64 = cnt.m_Int64 - m_Duration.m_Int64; + + return result; +} + + +inline int64 CFastTimer::GetClockSpeed() +{ + return g_ClockSpeed; +} + + +inline CCycleCount const& CFastTimer::GetDuration() const +{ +#ifdef DEBUG_FASTTIMER + assert( !m_bRunning ); +#endif + return m_Duration; +} + + +// -------------------------------------------------------------------------- // +// CAverageCycleCounter inlines + +inline CAverageCycleCounter::CAverageCycleCounter() + : m_nIters( 0 ) +{ +} + +inline void CAverageCycleCounter::Init() +{ + m_Total.Init(); + m_Peak.Init(); + m_nIters = 0; +} + +inline void CAverageCycleCounter::MarkIter( const CCycleCount &duration ) +{ + ++m_nIters; + m_Total += duration; + if ( m_Peak.IsLessThan( duration ) ) + m_Peak = duration; +} + +inline unsigned CAverageCycleCounter::GetIters() const +{ + return m_nIters; +} + +inline double CAverageCycleCounter::GetAverageMilliseconds() const +{ + if ( m_nIters ) + return (m_Total.GetMillisecondsF() / (double)m_nIters); + else + return 0; +} + +inline double CAverageCycleCounter::GetTotalMilliseconds() const +{ + return m_Total.GetMillisecondsF(); +} + +inline double CAverageCycleCounter::GetPeakMilliseconds() const +{ + return m_Peak.GetMillisecondsF(); +} + +// -------------------------------------------------------------------------- // + +inline CAverageTimeMarker::CAverageTimeMarker( CAverageCycleCounter *pCounter ) +{ + m_pCounter = pCounter; + m_Timer.Start(); +} + +inline CAverageTimeMarker::~CAverageTimeMarker() +{ + m_Timer.End(); + m_pCounter->MarkIter( m_Timer.GetDuration() ); +} + + +// CLimitTimer +// Use this to time whether a desired interval of time has passed. It's extremely fast +// to check while running. NOTE: CMicroSecOverage() and CMicroSecLeft() are not as fast to check. +class CLimitTimer +{ +public: + CLimitTimer() {} + CLimitTimer( uint64 cMicroSecDuration ) { SetLimit( cMicroSecDuration ); } + void SetLimit( uint64 m_cMicroSecDuration ); + bool BLimitReached() const; + + int CMicroSecOverage() const; + uint64 CMicroSecLeft() const; + +private: + uint64 m_lCycleLimit; +}; + + +//----------------------------------------------------------------------------- +// Purpose: Initializes the limit timer with a period of time to measure. +// Input : cMicroSecDuration - How long a time period to measure +//----------------------------------------------------------------------------- +inline void CLimitTimer::SetLimit( uint64 cMicroSecDuration ) +{ + uint64 dlCycles = ( ( uint64 ) cMicroSecDuration * g_ClockSpeed ) / ( uint64 ) 1000000L; + CCycleCount cycleCount; + cycleCount.Sample( ); + m_lCycleLimit = cycleCount.GetLongCycles( ) + dlCycles; +} + + +//----------------------------------------------------------------------------- +// Purpose: Determines whether our specified time period has passed +// Output: true if at least the specified time period has passed +//----------------------------------------------------------------------------- +inline bool CLimitTimer::BLimitReached() const +{ + CCycleCount cycleCount; + cycleCount.Sample( ); + return ( cycleCount.GetLongCycles( ) >= m_lCycleLimit ); +} + + +//----------------------------------------------------------------------------- +// Purpose: If we're over our specified time period, return the amount of the overage. +// Output: # of microseconds since we reached our specified time period. +//----------------------------------------------------------------------------- +inline int CLimitTimer::CMicroSecOverage() const +{ + CCycleCount cycleCount; + cycleCount.Sample(); + uint64 lcCycles = cycleCount.GetLongCycles(); + + if ( lcCycles < m_lCycleLimit ) + return 0; + + return( ( int ) ( ( lcCycles - m_lCycleLimit ) * ( uint64 ) 1000000L / g_ClockSpeed ) ); +} + + +//----------------------------------------------------------------------------- +// Purpose: If we're under our specified time period, return the amount under. +// Output: # of microseconds until we reached our specified time period, 0 if we've passed it +//----------------------------------------------------------------------------- +inline uint64 CLimitTimer::CMicroSecLeft() const +{ + CCycleCount cycleCount; + cycleCount.Sample(); + uint64 lcCycles = cycleCount.GetLongCycles(); + + if ( lcCycles >= m_lCycleLimit ) + return 0; + + return( ( uint64 ) ( ( m_lCycleLimit - lcCycles ) * ( uint64 ) 1000000L / g_ClockSpeed ) ); +} + + +#endif // FASTTIMER_H diff --git a/public/tier0/ia32detect.h b/public/tier0/ia32detect.h new file mode 100644 index 0000000..dd603d1 --- /dev/null +++ b/public/tier0/ia32detect.h @@ -0,0 +1,377 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +// +//=============================================================================// +#ifndef IA32DETECT_H +#define IA32DETECT_H + +#ifdef PLATFORM_WINDOWS_PC +#include <intrin.h> +#endif + +/* + This section from http://iss.cs.cornell.edu/ia32.htm + + + */ +typedef unsigned bit; + +enum CPUVendor +{ + INTEL, + AMD, + UNKNOWN_VENDOR +}; +class ia32detect +{ +public: + + enum type_t + { + type_OEM, + type_OverDrive, + type_Dual, + type_reserved + }; + + enum brand_t + { + brand_na, + brand_Celeron, + brand_PentiumIII, + brand_PentiumIIIXeon, + brand_reserved1, + brand_reserved2, + brand_PentiumIIIMobile, + brand_reserved3, + brand_Pentium4, + brand_invalid + }; + +# pragma pack(push, 1) + + struct version_t + { + bit Stepping : 4; + bit Model : 4; + bit Family : 4; + bit Type : 2; + bit Reserved1 : 2; + bit XModel : 4; + bit XFamily : 8; + bit Reserved2 : 4; + }; + + struct misc_t + { + byte Brand; + byte CLFLUSH; + byte Reserved; + byte APICId; + }; + + struct feature_t + { + bit FPU : 1; // Floating Point Unit On-Chip + bit VME : 1; // Virtual 8086 Mode Enhancements + bit DE : 1; // Debugging Extensions + bit PSE : 1; // Page Size Extensions + bit TSC : 1; // Time Stamp Counter + bit MSR : 1; // Model Specific Registers + bit PAE : 1; // Physical Address Extension + bit MCE : 1; // Machine Check Exception + bit CX8 : 1; // CMPXCHG8 Instruction + bit APIC : 1; // APIC On-Chip + bit Reserved1 : 1; + bit SEP : 1; // SYSENTER and SYSEXIT instructions + bit MTRR : 1; // Memory Type Range Registers + bit PGE : 1; // PTE Global Bit + bit MCA : 1; // Machine Check Architecture + bit CMOV : 1; // Conditional Move Instructions + bit PAT : 1; // Page Attribute Table + bit PSE36 : 1; // 32-bit Page Size Extension + bit PSN : 1; // Processor Serial Number + bit CLFSH : 1; // CLFLUSH Instruction + bit Reserved2 : 1; + bit DS : 1; // Debug Store + bit ACPI : 1; // Thermal Monitor and Software Controlled Clock Facilities + bit MMX : 1; // Intel MMX Technology + bit FXSR : 1; // FXSAVE and FXRSTOR Instructions + bit SSE : 1; // Intel SSE Technology + bit SSE2 : 1; // Intel SSE2 Technology + bit SS : 1; // Self Snoop + bit HTT : 1; // Hyper Threading + bit TM : 1; // Thermal Monitor + bit Reserved3 : 1; + bit PBE : 1; // Pending Brk. EN. + }; + +# pragma pack(pop) + + tstring vendor_name; + CPUVendor vendor; + tstring brand; + version_t version; + misc_t misc; + feature_t feature; + byte *cache; + + ia32detect () + { + + cache = 0; + uint32 m = init0(); + + uint32 *d = new uint32[m * 4]; + + for (uint32 i = 1; i <= m; i++) + { +#ifdef COMPILER_MSVC64 + __cpuid((int *) (d + (i-1) * 4), i); + +#else + uint32 *t = d + (i - 1) * 4; + + __asm + { + mov eax, i; + mov esi, t; + + cpuid; + + mov dword ptr [esi + 0x0], eax; + mov dword ptr [esi + 0x4], ebx; + mov dword ptr [esi + 0x8], ecx; + mov dword ptr [esi + 0xC], edx; + } +#endif + } + + if (m >= 1) + init1(d); + + if (m >= 2) + init2(d[4] & 0xFF); + + delete [] d; + + init0x80000000(); + + + //----------------------------------------------------------------------- + // Get the vendor of the processor + //----------------------------------------------------------------------- + if (_tcscmp(vendor_name.c_str(), _T("GenuineIntel")) == 0) + { + vendor = INTEL; + + } + else if (_tcscmp(vendor_name.c_str(), _T("AuthenticAMD")) == 0) + { + vendor = AMD; + + } + else + { + vendor = UNKNOWN_VENDOR; + } + } + + const tstring version_text () const + { + tchar b[128]; + + _stprintf(b, _T("%d.%d.%d %s XVersion(%d.%d)"), + version.Family, version.Model, version.Stepping, type_text(), version.XFamily, version.XModel); + + return tstring(b); + } + +protected: + + const tchar * type_text () const + { + static const tchar *text[] = + { + _T("Intel OEM Processor"), + _T("Intel OverDrive(R) Processor"), + _T("Intel Dual Processor"), + _T("reserved") + }; + + return text[version.Type]; + } + + const tstring brand_text () const + { + static const tchar *text[] = + { + _T("n/a"), + _T("Celeron"), + _T("Pentium III"), + _T("Pentium III Xeon"), + _T("reserved (4)"), + _T("reserved (5)"), + _T("Pentium III Mobile"), + _T("reserved (7)"), + _T("Pentium 4") + }; + + if (misc.Brand < brand_invalid) + return tstring(text[misc.Brand]); + else + { + tchar b[32]; + + _stprintf(b, _T("Brand %d (Update)"), misc.Brand); + + return tstring(b); + } + } + +private: + + uint32 init0 () + { + uint32 m; + + int data[4 + 1]; + tchar * s1; + + s1 = (tchar *) &data[1]; + __cpuid(data, 0); + data[4] = 0; + // Returns something like this: + // data[0] = 0x0000000b + // data[1] = 0x756e6547 Genu + // data[2] = 0x6c65746e ntel + // data[3] = 0x49656e69 ineI + // data[4] = 0x00000000 + + m = data[0]; + int t = data[2]; + data[2] = data[3]; + data[3] = t; + vendor_name = s1; + return m; + } + + void init1 (uint32 *d) + { + version = *(version_t *)&d[0]; + misc = *(misc_t *)&d[1]; + feature = *(feature_t *)&d[3]; + } + + void process2 (uint32 d, bool c[]) + { + if ((d & 0x80000000) == 0) + for (int i = 0; i < 32; i += 8) + c[(d >> i) & 0xFF] = true; + } + + void init2 (byte count) + { + uint32 d[4]; + bool c[256]; + + for (int ci1 = 0; ci1 < 256; ci1++) + c[ci1] = false; + + for (int i = 0; i < count; i++) + { +#ifdef COMPILER_MSVC64 + __cpuid((int *) d, 2); +#else + __asm + { + mov eax, 2; + lea esi, d; + cpuid; + mov [esi + 0x0], eax; + mov [esi + 0x4], ebx; + mov [esi + 0x8], ecx; + mov [esi + 0xC], edx; + } +#endif + + if (i == 0) + d[0] &= 0xFFFFFF00; + + process2(d[0], c); + process2(d[1], c); + process2(d[2], c); + process2(d[3], c); + } + + int m = 0; + + for (int ci2 = 0; ci2 < 256; ci2++) + if (c[ci2]) + m++; + + cache = new byte[m]; + + m = 0; + + for (int ci3 = 1; ci3 < 256; ci3++) + if (c[ci3]) + cache[m++] = ci3; + + cache[m] = 0; + } + + void init0x80000000 () + { + uint32 m; + +#ifdef COMPILER_MSVC64 + int data[4]; + __cpuid(data, 0x80000000); + m = data[0]; +#else + __asm + { + mov eax, 0x80000000; + cpuid; + mov m, eax + } +#endif + + if ((m & 0x80000000) != 0) + { + uint32 *d = new uint32[(m - 0x80000000) * 4]; + + for (uint32 i = 0x80000001; i <= m; i++) + { + uint32 *t = d + (i - 0x80000001) * 4; + +#ifdef COMPILER_MSVC64 + __cpuid((int *) (d + (i - 0x80000001) * 4), i); +#else + __asm + { + mov eax, i; + mov esi, t; + cpuid; + mov dword ptr [esi + 0x0], eax; + mov dword ptr [esi + 0x4], ebx; + mov dword ptr [esi + 0x8], ecx; + mov dword ptr [esi + 0xC], edx; + } +#endif + } + + if (m >= 0x80000002) + brand = (tchar *)(d + 4); + + // note the assignment to brand above does a copy, we need to delete + delete[] d; + } + } +}; + +#endif // IA32DETECT_H diff --git a/public/tier0/icommandline.h b/public/tier0/icommandline.h new file mode 100644 index 0000000..0cb54cf --- /dev/null +++ b/public/tier0/icommandline.h @@ -0,0 +1,61 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +//===========================================================================// + +#ifndef TIER0_ICOMMANDLINE_H +#define TIER0_ICOMMANDLINE_H +#ifdef _WIN32 +#pragma once +#endif + +#include "tier0/platform.h" + + +//----------------------------------------------------------------------------- +// Purpose: Interface to engine command line +//----------------------------------------------------------------------------- +abstract_class ICommandLine +{ +public: + virtual void CreateCmdLine( const char *commandline ) = 0; + virtual void CreateCmdLine( int argc, char **argv ) = 0; + virtual const char *GetCmdLine( void ) const = 0; + + // Check whether a particular parameter exists + virtual const char *CheckParm( const char *psz, const char **ppszValue = 0 ) const = 0; + // A bool return of whether param exists, useful for just checking if param that is just a flag is set + virtual bool HasParm( const char *psz ) const = 0; + virtual void RemoveParm( const char *parm ) = 0; + virtual void AppendParm( const char *pszParm, const char *pszValues ) = 0; + + // Returns the argument after the one specified, or the default if not found + virtual const char *ParmValue( const char *psz, const char *pDefaultVal = 0 ) const = 0; + virtual int ParmValue( const char *psz, int nDefaultVal ) const = 0; + virtual float ParmValue( const char *psz, float flDefaultVal ) const = 0; + + // Gets at particular parameters + virtual int ParmCount() const = 0; + virtual int FindParm( const char *psz ) const = 0; // Returns 0 if not found. + virtual const char* GetParm( int nIndex ) const = 0; + + // copies the string passwed + virtual void SetParm( int nIndex, char const *pNewParm ) =0; + + virtual const char *ParmValueByIndex( int nIndex, const char *pDefaultVal = 0 ) const = 0; +}; + +//----------------------------------------------------------------------------- +// Gets a singleton to the commandline interface +// NOTE: The #define trickery here is necessary for backwards compat: +// this interface used to lie in the vstdlib library. +//----------------------------------------------------------------------------- +PLATFORM_INTERFACE ICommandLine *CommandLine_Tier0(); + +#if !defined( VSTDLIB_BACKWARD_COMPAT ) +#define CommandLine CommandLine_Tier0 +#endif + +#endif // TIER0_ICOMMANDLINE_H + diff --git a/public/tier0/l2cache.h b/public/tier0/l2cache.h new file mode 100644 index 0000000..2ee8833 --- /dev/null +++ b/public/tier0/l2cache.h @@ -0,0 +1,46 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +//=============================================================================// +#ifndef CL2CACHE_H +#define CL2CACHE_H +#ifdef _WIN32 +#pragma once +#endif + +class P4Event_BSQ_cache_reference; + +class CL2Cache +{ +public: + + CL2Cache(); + ~CL2Cache(); + + void Start( void ); + void End( void ); + + //------------------------------------------------------------------------- + // GetL2CacheMisses + //------------------------------------------------------------------------- + int GetL2CacheMisses( void ) + { + return m_iL2CacheMissCount; + } + +#ifdef DBGFLAG_VALIDATE + void Validate( CValidator &validator, tchar *pchName ); // Validate our internal structures +#endif // DBGFLAG_VALIDATE + +private: + + int m_nID; + + P4Event_BSQ_cache_reference *m_pL2CacheEvent; + int64 m_i64Start; + int64 m_i64End; + int m_iL2CacheMissCount; +}; + +#endif // CL2CACHE_H diff --git a/public/tier0/mem.h b/public/tier0/mem.h new file mode 100644 index 0000000..3161241 --- /dev/null +++ b/public/tier0/mem.h @@ -0,0 +1,47 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: Memory allocation! +// +// $NoKeywords: $ +//=============================================================================// + +#ifndef TIER0_MEM_H +#define TIER0_MEM_H + +#ifdef _WIN32 +#pragma once +#endif + +#include <stddef.h> + +#include "tier0/platform.h" + +#if !defined(STATIC_TIER0) && !defined(_STATIC_LINKED) + +#ifdef TIER0_DLL_EXPORT +# define MEM_INTERFACE DLL_EXPORT +#else +# define MEM_INTERFACE DLL_IMPORT +#endif + +#else // BUILD_AS_DLL + +#define MEM_INTERFACE extern + +#endif // BUILD_AS_DLL + + + +//----------------------------------------------------------------------------- +// DLL-exported methods for particular kinds of memory +//----------------------------------------------------------------------------- +MEM_INTERFACE void *MemAllocScratch( int nMemSize ); +MEM_INTERFACE void MemFreeScratch(); +MEM_INTERFACE void MemAllocOOMError( size_t nSize ); + +#ifdef _LINUX +MEM_INTERFACE void ZeroMemory( void *mem, size_t length ); +#endif + + +#endif /* TIER0_MEM_H */ diff --git a/public/tier0/memalloc.h b/public/tier0/memalloc.h new file mode 100644 index 0000000..0a1c089 --- /dev/null +++ b/public/tier0/memalloc.h @@ -0,0 +1,661 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: This header should never be used directly from leaf code!!! +// Instead, just add the file memoverride.cpp into your project and all this +// will automagically be used +// +// $NoKeywords: $ +//=============================================================================// + +#ifndef TIER0_MEMALLOC_H +#define TIER0_MEMALLOC_H + +#ifdef _WIN32 +#pragma once +#endif + +// These memory debugging switches aren't relevant under Linux builds since memoverride.cpp +// isn't built into Linux projects +#ifndef POSIX +// Define this in release to get memory tracking even in release builds +//#define USE_MEM_DEBUG 1 +#endif + +#if defined( _MEMTEST ) +#ifdef _WIN32 +#define USE_MEM_DEBUG 1 +#endif +#endif + +// Undefine this if using a compiler lacking threadsafe RTTI (like vc6) +#define MEM_DEBUG_CLASSNAME 1 + +#include <stddef.h> +#if defined( OSX ) +#include <malloc/malloc.h> +#endif + +#include "tier0/mem.h" + +#if !defined(STEAM) && !defined(NO_MALLOC_OVERRIDE) + +struct _CrtMemState; + +#define MEMALLOC_VERSION 1 + +typedef size_t (*MemAllocFailHandler_t)( size_t ); + +//----------------------------------------------------------------------------- +// NOTE! This should never be called directly from leaf code +// Just use new,delete,malloc,free etc. They will call into this eventually +//----------------------------------------------------------------------------- +abstract_class IMemAlloc +{ +public: + // Release versions + virtual void *Alloc( size_t nSize ) = 0; + virtual void *Realloc( void *pMem, size_t nSize ) = 0; + virtual void Free( void *pMem ) = 0; + virtual void *Expand_NoLongerSupported( void *pMem, size_t nSize ) = 0; + + // Debug versions + virtual void *Alloc( size_t nSize, const char *pFileName, int nLine ) = 0; + virtual void *Realloc( void *pMem, size_t nSize, const char *pFileName, int nLine ) = 0; + virtual void Free( void *pMem, const char *pFileName, int nLine ) = 0; + virtual void *Expand_NoLongerSupported( void *pMem, size_t nSize, const char *pFileName, int nLine ) = 0; + + // Returns size of a particular allocation + virtual size_t GetSize( void *pMem ) = 0; + + // Force file + line information for an allocation + virtual void PushAllocDbgInfo( const char *pFileName, int nLine ) = 0; + virtual void PopAllocDbgInfo() = 0; + + // FIXME: Remove when we have our own allocator + // these methods of the Crt debug code is used in our codebase currently + virtual long CrtSetBreakAlloc( long lNewBreakAlloc ) = 0; + virtual int CrtSetReportMode( int nReportType, int nReportMode ) = 0; + virtual int CrtIsValidHeapPointer( const void *pMem ) = 0; + virtual int CrtIsValidPointer( const void *pMem, unsigned int size, int access ) = 0; + virtual int CrtCheckMemory( void ) = 0; + virtual int CrtSetDbgFlag( int nNewFlag ) = 0; + virtual void CrtMemCheckpoint( _CrtMemState *pState ) = 0; + + // FIXME: Make a better stats interface + virtual void DumpStats() = 0; + virtual void DumpStatsFileBase( char const *pchFileBase ) = 0; + + // FIXME: Remove when we have our own allocator + virtual void* CrtSetReportFile( int nRptType, void* hFile ) = 0; + virtual void* CrtSetReportHook( void* pfnNewHook ) = 0; + virtual int CrtDbgReport( int nRptType, const char * szFile, + int nLine, const char * szModule, const char * pMsg ) = 0; + + virtual int heapchk() = 0; + + virtual bool IsDebugHeap() = 0; + + virtual void GetActualDbgInfo( const char *&pFileName, int &nLine ) = 0; + virtual void RegisterAllocation( const char *pFileName, int nLine, int nLogicalSize, int nActualSize, unsigned nTime ) = 0; + virtual void RegisterDeallocation( const char *pFileName, int nLine, int nLogicalSize, int nActualSize, unsigned nTime ) = 0; + + virtual int GetVersion() = 0; + + virtual void CompactHeap() = 0; + + // Function called when malloc fails or memory limits hit to attempt to free up memory (can come in any thread) + virtual MemAllocFailHandler_t SetAllocFailHandler( MemAllocFailHandler_t pfnMemAllocFailHandler ) = 0; + + virtual void DumpBlockStats( void * ) = 0; + +#if defined( _MEMTEST ) + virtual void SetStatsExtraInfo( const char *pMapName, const char *pComment ) = 0; +#endif + + // Returns 0 if no failure, otherwise the size_t of the last requested chunk + // "I'm sure this is completely thread safe!" Brian Deen 7/19/2012. + virtual size_t MemoryAllocFailed() = 0; + + // handles storing allocation info for coroutines + virtual uint32 GetDebugInfoSize() = 0; + virtual void SaveDebugInfo( void *pvDebugInfo ) = 0; + virtual void RestoreDebugInfo( const void *pvDebugInfo ) = 0; + virtual void InitDebugInfo( void *pvDebugInfo, const char *pchRootFileName, int nLine ) = 0; + + // Replacement for ::GlobalMemoryStatus which accounts for unused memory in our system + virtual void GlobalMemoryStatus( size_t *pUsedMemory, size_t *pFreeMemory ) = 0; +}; + +//----------------------------------------------------------------------------- +// Singleton interface +//----------------------------------------------------------------------------- +MEM_INTERFACE IMemAlloc *g_pMemAlloc; + +//----------------------------------------------------------------------------- + +#ifdef MEMALLOC_REGIONS +#ifndef MEMALLOC_REGION +#define MEMALLOC_REGION 0 +#endif +inline void *MemAlloc_Alloc( size_t nSize ) +{ + return g_pMemAlloc->RegionAlloc( MEMALLOC_REGION, nSize ); +} + +inline void *MemAlloc_Alloc( size_t nSize, const char *pFileName, int nLine ) +{ + return g_pMemAlloc->RegionAlloc( MEMALLOC_REGION, nSize, pFileName, nLine ); +} +#else +#undef MEMALLOC_REGION +inline void *MemAlloc_Alloc( size_t nSize ) +{ + return g_pMemAlloc->Alloc( nSize ); +} + +inline void *MemAlloc_Alloc( size_t nSize, const char *pFileName, int nLine ) +{ + return g_pMemAlloc->Alloc( nSize, pFileName, nLine ); +} +#endif +inline void MemAlloc_Free( void *ptr ) +{ + g_pMemAlloc->Free( ptr ); +} +inline void MemAlloc_Free( void *ptr, const char *pFileName, int nLine ) +{ + g_pMemAlloc->Free( ptr, pFileName, nLine ); +} + +//----------------------------------------------------------------------------- + +inline bool ValueIsPowerOfTwo( size_t value ) // don't clash with mathlib definition +{ + return (value & ( value - 1 )) == 0; +} + +inline void *MemAlloc_AllocAligned( size_t size, size_t align ) +{ + unsigned char *pAlloc, *pResult; + + if (!IsPowerOfTwo(align)) + return NULL; + + align = (align > sizeof(void *) ? align : sizeof(void *)) - 1; + + if ( (pAlloc = (unsigned char*)g_pMemAlloc->Alloc( sizeof(void *) + align + size ) ) == (unsigned char*)NULL) + return NULL; + + pResult = (unsigned char*)( (size_t)(pAlloc + sizeof(void *) + align ) & ~align ); + ((unsigned char**)(pResult))[-1] = pAlloc; + + return (void *)pResult; +} + +inline void *MemAlloc_AllocAligned( size_t size, size_t align, const char *pszFile, int nLine ) +{ + unsigned char *pAlloc, *pResult; + + if (!IsPowerOfTwo(align)) + return NULL; + + align = (align > sizeof(void *) ? align : sizeof(void *)) - 1; + + if ( (pAlloc = (unsigned char*)g_pMemAlloc->Alloc( sizeof(void *) + align + size, pszFile, nLine ) ) == (unsigned char*)NULL) + return NULL; + + pResult = (unsigned char*)( (size_t)(pAlloc + sizeof(void *) + align ) & ~align ); + ((unsigned char**)(pResult))[-1] = pAlloc; + + return (void *)pResult; +} + +inline void *MemAlloc_AllocAlignedUnattributed( size_t size, size_t align ) +{ + unsigned char *pAlloc, *pResult; + + if (!ValueIsPowerOfTwo(align)) + return NULL; + + align = (align > sizeof(void *) ? align : sizeof(void *)) - 1; + + if ( (pAlloc = (unsigned char*)MemAlloc_Alloc( sizeof(void *) + align + size ) ) == (unsigned char*)NULL) + return NULL; + + pResult = (unsigned char*)( (size_t)(pAlloc + sizeof(void *) + align ) & ~align ); + ((unsigned char**)(pResult))[-1] = pAlloc; + + return (void *)pResult; +} + +inline void *MemAlloc_AllocAlignedFileLine( size_t size, size_t align, const char *pszFile, int nLine ) +{ + unsigned char *pAlloc, *pResult; + + if (!ValueIsPowerOfTwo(align)) + return NULL; + + align = (align > sizeof(void *) ? align : sizeof(void *)) - 1; + + if ( (pAlloc = (unsigned char*)MemAlloc_Alloc( sizeof(void *) + align + size, pszFile, nLine ) ) == (unsigned char*)NULL) + return NULL; + + pResult = (unsigned char*)( (size_t)(pAlloc + sizeof(void *) + align ) & ~align ); + ((unsigned char**)(pResult))[-1] = pAlloc; + + return (void *)pResult; +} + +inline void *MemAlloc_ReallocAligned( void *ptr, size_t size, size_t align ) +{ + if ( !IsPowerOfTwo( align ) ) + return NULL; + + // Don't change alignment between allocation + reallocation. + if ( ( (size_t)ptr & ( align - 1 ) ) != 0 ) + return NULL; + + if ( !ptr ) + return MemAlloc_AllocAligned( size, align ); + + void *pAlloc, *pResult; + + // Figure out the actual allocation point + pAlloc = ptr; + pAlloc = (void *)(((size_t)pAlloc & ~( sizeof(void *) - 1 ) ) - sizeof(void *)); + pAlloc = *( (void **)pAlloc ); + + // See if we have enough space + size_t nOffset = (size_t)ptr - (size_t)pAlloc; + size_t nOldSize = g_pMemAlloc->GetSize( pAlloc ); + if ( nOldSize >= size + nOffset ) + return ptr; + + pResult = MemAlloc_AllocAligned( size, align ); + memcpy( pResult, ptr, nOldSize - nOffset ); + g_pMemAlloc->Free( pAlloc ); + return pResult; +} + +inline void MemAlloc_FreeAligned( void *pMemBlock ) +{ + void *pAlloc; + + if ( pMemBlock == NULL ) + return; + + pAlloc = pMemBlock; + + // pAlloc points to the pointer to starting of the memory block + pAlloc = (void *)(((size_t)pAlloc & ~( sizeof(void *) - 1 ) ) - sizeof(void *)); + + // pAlloc is the pointer to the start of memory block + pAlloc = *( (void **)pAlloc ); + g_pMemAlloc->Free( pAlloc ); +} + +inline void MemAlloc_FreeAligned( void *pMemBlock, const char *pFileName, int nLine ) +{ + void *pAlloc; + + if ( pMemBlock == NULL ) + return; + + pAlloc = pMemBlock; + + // pAlloc points to the pointer to starting of the memory block + pAlloc = (void *)(((size_t)pAlloc & ~( sizeof(void *) - 1 ) ) - sizeof(void *)); + + // pAlloc is the pointer to the start of memory block + pAlloc = *( (void **)pAlloc ); + g_pMemAlloc->Free( pAlloc, pFileName, nLine ); +} + +inline size_t MemAlloc_GetSizeAligned( void *pMemBlock ) +{ + void *pAlloc; + + if ( pMemBlock == NULL ) + return 0; + + pAlloc = pMemBlock; + + // pAlloc points to the pointer to starting of the memory block + pAlloc = (void *)(((size_t)pAlloc & ~( sizeof(void *) - 1 ) ) - sizeof(void *)); + + // pAlloc is the pointer to the start of memory block + pAlloc = *((void **)pAlloc ); + return g_pMemAlloc->GetSize( pAlloc ) - ( (byte *)pMemBlock - (byte *)pAlloc ); +} + +//----------------------------------------------------------------------------- + +#if (defined(_DEBUG) || defined(USE_MEM_DEBUG)) +#define MEM_ALLOC_CREDIT_JOIN_AGAIN( a, b ) a ## b +#define MEM_ALLOC_CREDIT_JOIN( a, b ) MEM_ALLOC_CREDIT_JOIN_AGAIN( a, b ) +#define MEM_ALLOC_CREDIT_(tag) CMemAllocAttributeAlloction MEM_ALLOC_CREDIT_JOIN( memAllocAttributeAlloction, __LINE__ )( tag, __LINE__ ) +#define MemAlloc_PushAllocDbgInfo( pszFile, line ) g_pMemAlloc->PushAllocDbgInfo( pszFile, line ) +#define MemAlloc_PopAllocDbgInfo() g_pMemAlloc->PopAllocDbgInfo() +#define MemAlloc_RegisterAllocation( pFileName, nLine, nLogicalSize, nActualSize, nTime ) g_pMemAlloc->RegisterAllocation( pFileName, nLine, nLogicalSize, nActualSize, nTime ) +#define MemAlloc_RegisterDeallocation( pFileName, nLine, nLogicalSize, nActualSize, nTime ) g_pMemAlloc->RegisterDeallocation( pFileName, nLine, nLogicalSize, nActualSize, nTime ) +#else +#define MEM_ALLOC_CREDIT_(tag) ((void)0) +#define MemAlloc_PushAllocDbgInfo( pszFile, line ) ((void)0) +#define MemAlloc_PopAllocDbgInfo() ((void)0) +#define MemAlloc_RegisterAllocation( pFileName, nLine, nLogicalSize, nActualSize, nTime ) ((void)0) +#define MemAlloc_RegisterDeallocation( pFileName, nLine, nLogicalSize, nActualSize, nTime ) ((void)0) +#endif + +#define MemAlloc_DumpStats() g_pMemAlloc->DumpStats() +#define MemAlloc_CompactHeap() g_pMemAlloc->CompactHeap() +#define MemAlloc_CompactIncremental() g_pMemAlloc->CompactIncremental() +#define MemAlloc_DumpStatsFileBase( _filename ) g_pMemAlloc->DumpStatsFileBase( _filename ) +#define MemAlloc_CrtCheckMemory() g_pMemAlloc->CrtCheckMemory() +#define MemAlloc_GlobalMemoryStatus( _usedMemory, _freeMemory ) g_pMemAlloc->GlobalMemoryStatus( _usedMemory, _freeMemory ) +#define MemAlloc_MemoryAllocFailed() g_pMemAlloc->MemoryAllocFailed() + +#define MemAlloc_GetDebugInfoSize() g_pMemAlloc->GetDebugInfoSize() +#define MemAlloc_SaveDebugInfo( pvDebugInfo ) g_pMemAlloc->SaveDebugInfo( pvDebugInfo ) +#define MemAlloc_RestoreDebugInfo( pvDebugInfo ) g_pMemAlloc->RestoreDebugInfo( pvDebugInfo ) +#define MemAlloc_InitDebugInfo( pvDebugInfo, pchRootFileName, nLine ) g_pMemAlloc->InitDebugInfo( pvDebugInfo, pchRootFileName, nLine ) +#define MemAlloc_GetSize( x ) g_pMemAlloc->GetSize( x ); +//----------------------------------------------------------------------------- + +class CMemAllocAttributeAlloction +{ +public: + CMemAllocAttributeAlloction( const char *pszFile, int line ) + { + MemAlloc_PushAllocDbgInfo( pszFile, line ); + } + + ~CMemAllocAttributeAlloction() + { + MemAlloc_PopAllocDbgInfo(); + } +}; + +#define MEM_ALLOC_CREDIT() MEM_ALLOC_CREDIT_(__FILE__) + +//----------------------------------------------------------------------------- + +#if defined(_WIN32) && ( defined(_DEBUG) || defined(USE_MEM_DEBUG) ) + + #pragma warning(disable:4290) + #pragma warning(push) + #include <typeinfo.h> + + // MEM_DEBUG_CLASSNAME is opt-in. + // Note: typeid().name() is not threadsafe, so if the project needs to access it in multiple threads + // simultaneously, it'll need a mutex. + #if defined(_CPPRTTI) && defined(MEM_DEBUG_CLASSNAME) + #define MEM_ALLOC_CREDIT_CLASS() MEM_ALLOC_CREDIT_( typeid(*this).name() ) + #define MEM_ALLOC_CLASSNAME(type) (typeid((type*)(0)).name()) + #else + #define MEM_ALLOC_CREDIT_CLASS() MEM_ALLOC_CREDIT_( __FILE__ ) + #define MEM_ALLOC_CLASSNAME(type) (__FILE__) + #endif + + // MEM_ALLOC_CREDIT_FUNCTION is used when no this pointer is available ( inside 'new' overloads, for example ) + #ifdef _MSC_VER + #define MEM_ALLOC_CREDIT_FUNCTION() MEM_ALLOC_CREDIT_( __FUNCTION__ ) + #else + #define MEM_ALLOC_CREDIT_FUNCTION() (__FILE__) + #endif + + #pragma warning(pop) +#else + #define MEM_ALLOC_CREDIT_CLASS() + #define MEM_ALLOC_CLASSNAME(type) NULL + #define MEM_ALLOC_CREDIT_FUNCTION() +#endif + +//----------------------------------------------------------------------------- + +#if (defined(_DEBUG) || defined(USE_MEM_DEBUG)) +struct MemAllocFileLine_t +{ + const char *pszFile; + int line; +}; + +#define MEMALLOC_DEFINE_EXTERNAL_TRACKING( tag ) \ + static CUtlMap<void *, MemAllocFileLine_t, int> g_##tag##Allocs( DefLessFunc( void *) ); \ + static const char *g_psz##tag##Alloc = strcpy( (char *)g_pMemAlloc->Alloc( strlen( #tag "Alloc" ) + 1, "intentional leak", 0 ), #tag "Alloc" ); + +#define MemAlloc_RegisterExternalAllocation( tag, p, size ) \ + if ( !p ) \ + ; \ + else \ + { \ + MemAllocFileLine_t fileLine = { g_psz##tag##Alloc, 0 }; \ + g_pMemAlloc->GetActualDbgInfo( fileLine.pszFile, fileLine.line ); \ + if ( fileLine.pszFile != g_psz##tag##Alloc ) \ + { \ + g_##tag##Allocs.Insert( p, fileLine ); \ + } \ + \ + MemAlloc_RegisterAllocation( fileLine.pszFile, fileLine.line, size, size, 0); \ + } + +#define MemAlloc_RegisterExternalDeallocation( tag, p, size ) \ + if ( !p ) \ + ; \ + else \ + { \ + MemAllocFileLine_t fileLine = { g_psz##tag##Alloc, 0 }; \ + CUtlMap<void *, MemAllocFileLine_t, int>::IndexType_t iRecordedFileLine = g_##tag##Allocs.Find( p ); \ + if ( iRecordedFileLine != g_##tag##Allocs.InvalidIndex() ) \ + { \ + fileLine = g_##tag##Allocs[iRecordedFileLine]; \ + g_##tag##Allocs.RemoveAt( iRecordedFileLine ); \ + } \ + \ + MemAlloc_RegisterDeallocation( fileLine.pszFile, fileLine.line, size, size, 0); \ + } + +#else + +#define MEMALLOC_DEFINE_EXTERNAL_TRACKING( tag ) +#define MemAlloc_RegisterExternalAllocation( tag, p, size ) ((void)0) +#define MemAlloc_RegisterExternalDeallocation( tag, p, size ) ((void)0) + +#endif + +//----------------------------------------------------------------------------- + +#elif defined( POSIX ) + +inline void MemAlloc_CheckAlloc( void *ptr, size_t nSize ) +{ + if ( !ptr ) + MemAllocOOMError( nSize ); +} + +#if defined( OSX ) +// Mac always aligns allocs, don't need to call posix_memalign which doesn't exist in 10.5.8 which TF2 still needs to run on +//inline void *memalign(size_t alignment, size_t size) {void *pTmp=NULL; posix_memalign(&pTmp, alignment, size); return pTmp;} +inline void *memalign(size_t alignment, size_t size) {void *pTmp=NULL; pTmp = malloc(size); MemAlloc_CheckAlloc( pTmp, size ); return pTmp;} +#endif + +inline void *_aligned_malloc( size_t nSize, size_t align ) { void *ptr = memalign( align, nSize ); MemAlloc_CheckAlloc( ptr, nSize ); return ptr; } +inline void _aligned_free( void *ptr ) { free( ptr ); } + +inline void *MemAlloc_Alloc( size_t nSize, const char *pFileName = NULL, int nLine = 0 ) { void *ptr = malloc( nSize ); MemAlloc_CheckAlloc( ptr, nSize ); return ptr; } +inline void MemAlloc_Free( void *ptr, const char *pFileName = NULL, int nLine = 0 ) { free( ptr ); } + +inline void *MemAlloc_AllocAligned( size_t size, size_t align, const char *pszFile = NULL, int nLine = 0 ) { void *ptr = memalign( align, size ); MemAlloc_CheckAlloc( ptr, size ); return ptr; } +inline void *MemAlloc_AllocAlignedFileLine( size_t size, size_t align, const char *pszFile = NULL, int nLine = 0 ) { void *ptr = memalign( align, size ); MemAlloc_CheckAlloc( ptr, size ); return ptr; } +inline void MemAlloc_FreeAligned( void *pMemBlock, const char *pszFile = NULL, int nLine = 0 ) { free( pMemBlock ); } + +#if defined( OSX ) +inline size_t _msize( void *ptr ) { return malloc_size( ptr ); } +#else +inline size_t _msize( void *ptr ) { return malloc_usable_size( ptr ); } +#endif + +inline void *MemAlloc_ReallocAligned( void *ptr, size_t size, size_t align ) +{ + void *ptr_new_aligned = memalign( align, size ); + + if( ptr_new_aligned ) + { + size_t old_size = _msize( ptr ); + size_t copy_size = ( size < old_size ) ? size : old_size; + + memcpy( ptr_new_aligned, ptr, copy_size ); + free( ptr ); + } + + MemAlloc_CheckAlloc( ptr_new_aligned, size ); + return ptr_new_aligned; +} +#else +#define MemAlloc_GetDebugInfoSize() g_pMemAlloc->GetDebugInfoSize() +#define MemAlloc_SaveDebugInfo( pvDebugInfo ) g_pMemAlloc->SaveDebugInfo( pvDebugInfo ) +#define MemAlloc_RestoreDebugInfo( pvDebugInfo ) g_pMemAlloc->RestoreDebugInfo( pvDebugInfo ) +#define MemAlloc_InitDebugInfo( pvDebugInfo, pchRootFileName, nLine ) g_pMemAlloc->InitDebugInfo( pvDebugInfo, pchRootFileName, nLine ) + +#endif // !STEAM && !NO_MALLOC_OVERRIDE + +//----------------------------------------------------------------------------- + +#if !defined(STEAM) && defined(NO_MALLOC_OVERRIDE) + +#define MEM_ALLOC_CREDIT_(tag) ((void)0) +#define MEM_ALLOC_CREDIT() MEM_ALLOC_CREDIT_(__FILE__) +#define MEM_ALLOC_CREDIT_FUNCTION() +#define MEM_ALLOC_CREDIT_CLASS() +#define MEM_ALLOC_CLASSNAME(type) NULL + +#define MemAlloc_PushAllocDbgInfo( pszFile, line ) +#define MemAlloc_PopAllocDbgInfo() +#define MemAlloc_RegisterAllocation( pFileName, nLine, nLogicalSize, nActualSize, nTime ) ((void)0) +#define MemAlloc_RegisterDeallocation( pFileName, nLine, nLogicalSize, nActualSize, nTime ) ((void)0) +#define MemAlloc_DumpStats() ((void)0) +#define MemAlloc_CompactHeap() ((void)0) +#define MemAlloc_CompactIncremental() ((void)0) +#define MemAlloc_DumpStatsFileBase( _filename ) ((void)0) +inline bool MemAlloc_CrtCheckMemory() { return true; } +inline void MemAlloc_GlobalMemoryStatus( size_t *pusedMemory, size_t *pfreeMemory ) +{ + *pusedMemory = 0; + *pfreeMemory = 0; +} +#define MemAlloc_MemoryAllocFailed() 0 + +#define MemAlloc_GetDebugInfoSize() 0 +#define MemAlloc_SaveDebugInfo( pvDebugInfo ) ((void)0) +#define MemAlloc_RestoreDebugInfo( pvDebugInfo ) ((void)0) +#define MemAlloc_InitDebugInfo( pvDebugInfo, pchRootFileName, nLine ) ((void)0) + + +#define MEMALLOC_DEFINE_EXTERNAL_TRACKING( tag ) +#define MemAlloc_RegisterExternalAllocation( tag, p, size ) ((void)0) +#define MemAlloc_RegisterExternalDeallocation( tag, p, size ) ((void)0) + +#endif // !STEAM && NO_MALLOC_OVERRIDE + +//----------------------------------------------------------------------------- + + + +// linux memory tracking via hooks. +#if defined( POSIX ) && !defined( NO_HOOK_MALLOC ) +PLATFORM_INTERFACE void MemoryLogMessage( char const *s ); // throw a message into the memory log +PLATFORM_INTERFACE void EnableMemoryLogging( bool bOnOff ); +PLATFORM_INTERFACE void DumpMemoryLog( int nThresh ); +PLATFORM_INTERFACE void DumpMemorySummary( void ); +PLATFORM_INTERFACE void SetMemoryMark( void ); +PLATFORM_INTERFACE void DumpChangedMemory( int nThresh ); + +#else +FORCEINLINE void MemoryLogMessage( char const *s ) +{ +} + +FORCEINLINE void EnableMemoryLogging( bool bOnOff ) +{ +} +FORCEINLINE void DumpMemoryLog( int nThresh ) +{ +} +FORCEINLINE void DumpMemorySummary( void ) +{ +} +FORCEINLINE void SetMemoryMark( void ) +{ +} +FORCEINLINE void DumpChangedMemory( int nThresh ) +{ +} + +#endif + +#ifdef POSIX +// ApproximateProcessMemoryUsage returns the approximate memory footprint of this process. +PLATFORM_INTERFACE size_t ApproximateProcessMemoryUsage( void ); +#else +FORCEINLINE size_t ApproximateProcessMemoryUsage( void ) +{ + return 0; +} + +#endif + +struct aligned_tmp_t +{ + // empty base class +}; + +/* +This class used to be required if you wanted an object to be allocated with a specific +alignment. ALIGN16 and ALIGN16_POST are not actually sufficient for this because they +guarantee that the globals, statics, locals, and function parameters are appropriately +aligned they do not affect memory allocation alignment. +However this class is usually not needed because as of 2012 our policy is that our +allocator should take care of this automatically. Any object whose size is a multiple +of 16 will be 16-byte aligned. Existing uses of this class were not changed because +the cost/benefit did not justify it. +*/ +// template here to allow adding alignment at levels of hierarchy that aren't the base +template< int bytesAlignment = 16, class T = aligned_tmp_t > +class CAlignedNewDelete : public T +{ + +public: + /* + Note that this class does not overload operator new[] and delete[] which means that + classes that depend on this for alignment may end up misaligned if an array is + allocated. This problem is now mostly theoretical because this class is mostly + obsolete. + */ + void *operator new( size_t nSize ) + { + return MemAlloc_AllocAligned( nSize, bytesAlignment ); + } + + void* operator new( size_t nSize, int nBlockUse, const char *pFileName, int nLine ) + { + return MemAlloc_AllocAlignedFileLine( nSize, bytesAlignment, pFileName, nLine ); + } + + void operator delete(void *pData) + { + if ( pData ) + { + MemAlloc_FreeAligned( pData ); + } + } + + void operator delete( void* pData, int nBlockUse, const char *pFileName, int nLine ) + { + if ( pData ) + { + MemAlloc_FreeAligned( pData, pFileName, nLine ); + } + } +}; + + +#endif /* TIER0_MEMALLOC_H */ diff --git a/public/tier0/memdbgoff.h b/public/tier0/memdbgoff.h new file mode 100644 index 0000000..1cd09da --- /dev/null +++ b/public/tier0/memdbgoff.h @@ -0,0 +1,25 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: This header, which must be the final line of a .h file, +// causes all crt methods to stop using debugging versions of the memory allocators. +// NOTE: Use memdbgon.h to re-enable memory debugging. +// +// $NoKeywords: $ +//=============================================================================// + +#ifdef MEM_OVERRIDE_ON + +#undef malloc +#undef realloc +#undef calloc +#undef free +#undef _expand +#undef _msize +#undef new +#undef _aligned_malloc +#undef _aligned_free +#undef _malloc_dbg + +#undef MEM_OVERRIDE_ON + +#endif diff --git a/public/tier0/memdbgon.h b/public/tier0/memdbgon.h new file mode 100644 index 0000000..9f022a0 --- /dev/null +++ b/public/tier0/memdbgon.h @@ -0,0 +1,318 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: This header, which must be the final include in a .cpp (or .h) file, +// causes all crt methods to use debugging versions of the memory allocators. +// NOTE: Use memdbgoff.h to disable memory debugging. +// +// $NoKeywords: $ +//=============================================================================// + +// SPECIAL NOTE! This file must *not* use include guards; we need to be able +// to include this potentially multiple times (since we can deactivate debugging +// by including memdbgoff.h) + +#if !defined(STEAM) && !defined(NO_MALLOC_OVERRIDE) + +// SPECIAL NOTE #2: This must be the final include in a .cpp or .h file!!! + +#if defined(_DEBUG) && !defined(USE_MEM_DEBUG) +#define USE_MEM_DEBUG 1 +#endif + +// If debug build or ndebug and not already included MS custom alloc files, or already included this file +#if (defined(_DEBUG) || !defined(_INC_CRTDBG)) || defined(MEMDBGON_H) + +#include "basetypes.h" +#ifdef _WIN32 +#include <tchar.h> +#else +#include <wchar.h> +#endif +#include <string.h> +#include <malloc.h> +#include "commonmacros.h" +#include "memalloc.h" + +#if defined(USE_MEM_DEBUG) + #if defined( POSIX ) + + #define _NORMAL_BLOCK 1 + + #include <cstddef> + #include <glob.h> + #include <new> + #include <sys/types.h> + #if !defined( DID_THE_OPERATOR_NEW ) + #define DID_THE_OPERATOR_NEW + // posix doesn't have a new of this form, so we impl our own + #ifdef OSX + void* operator new( size_t nSize, int blah, const char *pFileName, int nLine ) throw (std::bad_alloc); + void* operator new[]( size_t nSize, int blah, const char *pFileName, int nLine ) throw (std::bad_alloc); + #else + void* operator new( size_t nSize, int blah, const char *pFileName, int nLine ); + void* operator new[]( size_t nSize, int blah, const char *pFileName, int nLine ); + #endif + #endif + + #else // defined(POSIX) + + // Include crtdbg.h and make sure _DEBUG is set to 1. + #if !defined(_DEBUG) + #define _DEBUG 1 + #include <crtdbg.h> + #undef _DEBUG + #else + #include <crtdbg.h> + #endif // !defined(_DEBUG) + + #endif // defined(POSIX) +#endif + +#include "tier0/memdbgoff.h" + +// -------------------------------------------------------- +// Debug/non-debug agnostic elements + +#define MEM_OVERRIDE_ON 1 + +#undef malloc +#undef realloc +#undef calloc +#undef _expand +#undef free +#undef _msize +#undef _aligned_malloc +#undef _aligned_free + +#ifndef MEMDBGON_H +inline void *MemAlloc_InlineCallocMemset( void *pMem, size_t nCount, size_t nElementSize) +{ + memset(pMem, 0, nElementSize * nCount); + return pMem; +} +#endif + +#define calloc(c, s) MemAlloc_InlineCallocMemset(malloc(c*s), c, s) +#define free(p) g_pMemAlloc->Free( p ) +#define _msize(p) g_pMemAlloc->GetSize( p ) +#define _expand(p, s) _expand_NoLongerSupported(p, s) +#define _aligned_free( p ) MemAlloc_FreeAligned( p ) + +// -------------------------------------------------------- +// Debug path +#if defined(USE_MEM_DEBUG) + +#define malloc(s) g_pMemAlloc->Alloc( s, __FILE__, __LINE__) +#define realloc(p, s) g_pMemAlloc->Realloc( p, s, __FILE__, __LINE__ ) +#define _aligned_malloc( s, a ) MemAlloc_AllocAligned( s, a, __FILE__, __LINE__ ) + +#ifdef _malloc_dbg +#undef _malloc_dbg +#endif +#define _malloc_dbg(s, t, f, l) WHYCALLINGTHISDIRECTLY(s) + +#if !defined( POSIX ) +#if defined(__AFX_H__) && defined(DEBUG_NEW) + #define new DEBUG_NEW +#else + #undef new + #define MEMALL_DEBUG_NEW new(_NORMAL_BLOCK, __FILE__, __LINE__) + #define new MEMALL_DEBUG_NEW +#endif +#endif + +#undef _strdup +#undef strdup +#undef _wcsdup +#undef wcsdup + +#define _strdup(s) MemAlloc_StrDup(s, __FILE__, __LINE__) +#define strdup(s) MemAlloc_StrDup(s, __FILE__, __LINE__) +#define _wcsdup(s) MemAlloc_WcStrDup(s, __FILE__, __LINE__) +#define wcsdup(s) MemAlloc_WcStrDup(s, __FILE__, __LINE__) + +// Make sure we don't define strdup twice +#if !defined(MEMDBGON_H) + +inline char *MemAlloc_StrDup(const char *pString, const char *pFileName, unsigned nLine) +{ + char *pMemory; + + if (!pString) + return NULL; + + size_t len = strlen(pString) + 1; + if ((pMemory = (char *)g_pMemAlloc->Alloc(len, pFileName, nLine)) != NULL) + { + return strcpy( pMemory, pString ); + } + + return NULL; +} + +inline wchar_t *MemAlloc_WcStrDup(const wchar_t *pString, const char *pFileName, unsigned nLine) +{ + wchar_t *pMemory; + + if (!pString) + return NULL; + + size_t len = (wcslen(pString) + 1); + if ((pMemory = (wchar_t *)g_pMemAlloc->Alloc(len * sizeof(wchar_t), pFileName, nLine)) != NULL) + { + return wcscpy( pMemory, pString ); + } + + return NULL; +} + +#endif // DBMEM_DEFINED_STRDUP + +#else +// -------------------------------------------------------- +// Release path + +#define malloc(s) g_pMemAlloc->Alloc( s ) +#define realloc(p, s) g_pMemAlloc->Realloc( p, s ) +#define _aligned_malloc( s, a ) MemAlloc_AllocAligned( s, a ) + +#ifdef _malloc_dbg +#undef _malloc_dbg +#endif +#define _malloc_dbg(s, t, f, l) WHYCALLINGTHISDIRECTLY(s) + +#undef new + +#undef _strdup +#undef strdup +#undef _wcsdup +#undef wcsdup + +#define _strdup(s) MemAlloc_StrDup(s) +#define strdup(s) MemAlloc_StrDup(s) +#define _wcsdup(s) MemAlloc_WcStrDup(s) +#define wcsdup(s) MemAlloc_WcStrDup(s) + +// Make sure we don't define strdup twice +#if !defined(MEMDBGON_H) + +inline char *MemAlloc_StrDup(const char *pString) +{ + char *pMemory; + + if (!pString) + return NULL; + + size_t len = strlen(pString) + 1; + if ((pMemory = (char *)g_pMemAlloc->Alloc(len)) != NULL) + { + return strcpy( pMemory, pString ); + } + + return NULL; +} + +inline wchar_t *MemAlloc_WcStrDup(const wchar_t *pString) +{ + wchar_t *pMemory; + + if (!pString) + return NULL; + + size_t len = (wcslen(pString) + 1); + if ((pMemory = (wchar_t *)g_pMemAlloc->Alloc(len * sizeof(wchar_t))) != NULL) + { + return wcscpy( pMemory, pString ); + } + + return NULL; +} + +#endif // DBMEM_DEFINED_STRDUP + +#endif // USE_MEM_DEBUG + +#define MEMDBGON_H // Defined here so can be used above + +#else + +#if defined(USE_MEM_DEBUG) +#ifndef _STATIC_LINKED +#pragma message ("Note: file includes crtdbg.h directly, therefore will cannot use memdbgon.h in non-debug build") +#else +#error "Error: file includes crtdbg.h directly, therefore will cannot use memdbgon.h in non-debug build. Not recoverable in static build" +#endif +#endif +#endif // _INC_CRTDBG + +#else + +// Needed for MEM_ALLOC_CREDIT(), MemAlloc_Alloc(), etc. + +#include "memalloc.h" + +#if !defined( VALVE_ALLOCS_DEFINED ) +#define VALVE_ALLOCS_DEFINED + +#include "tier0/mem.h" + +inline void *valve_malloc_check_oom( size_t size ) +{ + void *ptr = malloc( size ); + if ( !ptr ) + MemAllocOOMError( size ); + return ptr; +} + +inline void *valve_realloc_check_oom( void *ptr, size_t size ) +{ + void *ret = realloc( ptr, size ); + if ( !ret ) + MemAllocOOMError( size ); + return ret; +} + +inline void *valve_calloc_check_oom( size_t num, size_t size ) +{ + void *ptr = calloc( num, size ); + if (!ptr) + MemAllocOOMError( num * size ); + return ptr; +} + +inline void *valve_memalign_check_oom( size_t alignment, size_t size ) +{ + void *ptr = memalign( alignment, size ); + if (!ptr) + MemAllocOOMError(size); + return ptr; +} + +inline void *valve_aligned_malloc_check_oom( size_t size, size_t alignment ) +{ + void *ptr = _aligned_malloc( size, alignment ); + if (!ptr) + MemAllocOOMError( size ); + return ptr; +} + + +#endif // VALVE_ALLOCS_DEFINED + +#define malloc valve_malloc_check_oom +#define realloc valve_realloc_check_oom +#define calloc valve_calloc_check_oom +#define memalign valve_memalign_check_oom +#define _aligned_malloc valve_aligned_malloc_check_oom + +#endif // !STEAM && !NO_MALLOC_OVERRIDE + +#if defined( NO_MALLOC_OVERRIDE ) + +#undef malloc +#undef realloc +#undef calloc +#undef memalign +#undef _aligned_malloc + +#endif // NO_MALLOC_OVERRIDE diff --git a/public/tier0/memoverride.cpp b/public/tier0/memoverride.cpp new file mode 100644 index 0000000..925f9d2 --- /dev/null +++ b/public/tier0/memoverride.cpp @@ -0,0 +1,1462 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: Insert this file into all projects using the memory system +// It will cause that project to use the shader memory allocator +// +// $NoKeywords: $ +//=============================================================================// + + +#if !defined(STEAM) && !defined(NO_MALLOC_OVERRIDE) + +#undef PROTECTED_THINGS_ENABLE // allow use of _vsnprintf + +#if defined( _WIN32 ) && !defined( _X360 ) +#define WIN_32_LEAN_AND_MEAN +#include <windows.h> +#endif + +#ifdef _WIN32 +// ARG: crtdbg is necessary for certain definitions below, +// but it also redefines malloc as a macro in release. +// To disable this, we gotta define _DEBUG before including it.. BLEAH! +#define _DEBUG 1 +#include "crtdbg.h" +#ifdef NDEBUG +#undef _DEBUG +#endif +// Turn this back off in release mode. +#ifdef NDEBUG +#undef _DEBUG +#endif +#endif + +#include "tier0/dbg.h" +#include "tier0/memalloc.h" +#include <string.h> +#include <stdio.h> +#include "memdbgoff.h" + + +#if POSIX +#define __cdecl +#endif + +#if defined( _WIN32 ) && !defined( _X360 ) +const char *MakeModuleFileName() +{ + if ( g_pMemAlloc && g_pMemAlloc->IsDebugHeap() ) + { + char *pszModuleName = (char *)HeapAlloc( GetProcessHeap(), 0, MAX_PATH ); // small leak, debug only + + MEMORY_BASIC_INFORMATION mbi; + static int dummy; + VirtualQuery( &dummy, &mbi, sizeof(mbi) ); + + GetModuleFileName( reinterpret_cast<HMODULE>(mbi.AllocationBase), pszModuleName, MAX_PATH ); + char *pDot = strrchr( pszModuleName, '.' ); + if ( pDot ) + { + char *pSlash = strrchr( pszModuleName, '\\' ); + if ( pSlash ) + { + pszModuleName = pSlash + 1; + *pDot = 0; + } + } + + return pszModuleName; + } + return NULL; +} + +//----------------------------------------------------------------------------- +// Purpose: helper class to detect when static construction has been done by the CRT +//----------------------------------------------------------------------------- +class CStaticConstructionCheck +{ +public: + volatile bool m_bConstructed = true; +}; + +static CStaticConstructionCheck s_CheckStaticsConstructed; + +const char *GetModuleFileName() +{ +#if !defined(_MSC_VER) || ( _MSC_VER >= 1900 ) // VC 2015 and above, with the UCRT, will crash if you use a static before it is constructed + if ( !s_CheckStaticsConstructed.m_bConstructed ) + return nullptr; +#endif + + static const char *pszOwner = MakeModuleFileName(); + return pszOwner; +} + + +static void *AllocUnattributed( size_t nSize ) +{ + const char *pszOwner = GetModuleFileName(); + + if ( !pszOwner ) + return g_pMemAlloc->Alloc(nSize); + else + return g_pMemAlloc->Alloc(nSize, pszOwner, 0); +} + +static void *ReallocUnattributed( void *pMem, size_t nSize ) +{ + const char *pszOwner = GetModuleFileName(); + + if ( !pszOwner ) + return g_pMemAlloc->Realloc(pMem, nSize); + else + return g_pMemAlloc->Realloc(pMem, nSize, pszOwner, 0); +} + +#else +#define MakeModuleFileName() NULL +inline void *AllocUnattributed( size_t nSize ) +{ + return g_pMemAlloc->Alloc(nSize); +} + +inline void *ReallocUnattributed( void *pMem, size_t nSize ) +{ + return g_pMemAlloc->Realloc(pMem, nSize); +} +#endif + +//----------------------------------------------------------------------------- +// Standard functions in the CRT that we're going to override to call our allocator +//----------------------------------------------------------------------------- +#if defined(_WIN32) && !defined(_STATIC_LINKED) +// this magic only works under win32 +// under linux this malloc() overrides the libc malloc() and so we +// end up in a recursion (as g_pMemAlloc->Alloc() calls malloc) +#if _MSC_VER >= 1900 +#define SUPPRESS_INVALID_PARAMETER_NO_INFO +#define ALLOC_CALL __declspec(restrict) +#define FREE_CALL +#elif _MSC_VER >= 1400 +#define ALLOC_CALL _CRTNOALIAS _CRTRESTRICT +#define FREE_CALL _CRTNOALIAS +#else +#define ALLOC_CALL +#define FREE_CALL +#endif + +extern "C" +{ + +ALLOC_CALL void *malloc( size_t nSize ) +{ + return AllocUnattributed( nSize ); +} + +FREE_CALL void free( void *pMem ) +{ + g_pMemAlloc->Free(pMem); +} + +ALLOC_CALL void *realloc( void *pMem, size_t nSize ) +{ + return ReallocUnattributed( pMem, nSize ); +} + +ALLOC_CALL void *calloc( size_t nCount, size_t nElementSize ) +{ + void *pMem = AllocUnattributed( nElementSize * nCount ); + memset(pMem, 0, nElementSize * nCount); + return pMem; +} + +} // end extern "C" + +//----------------------------------------------------------------------------- +// Non-standard MSVC functions that we're going to override to call our allocator +//----------------------------------------------------------------------------- +extern "C" +{ + +// 64-bit +#ifdef _WIN64 +void* __cdecl _malloc_base( size_t nSize ) +{ + return AllocUnattributed( nSize ); +} +#else +ALLOC_CALL void *_malloc_base( size_t nSize ) +{ + return AllocUnattributed( nSize ); +} +#endif + +ALLOC_CALL void *_calloc_base( size_t nCount, size_t nSize ) +{ + void *pMem = AllocUnattributed( nSize*nCount ); + memset(pMem, 0, nSize*nCount ); + return pMem; +} + +ALLOC_CALL void *_realloc_base( void *pMem, size_t nSize ) +{ + return ReallocUnattributed( pMem, nSize ); +} + +ALLOC_CALL void *_recalloc_base( void *pMem, size_t nSize ) +{ + void *pMemOut = ReallocUnattributed( pMem, nSize ); + if ( !pMem ) + { + memset( pMemOut, 0, nSize ); + } + return pMemOut; +} + +void _free_base( void *pMem ) +{ + g_pMemAlloc->Free(pMem); +} + +void *__cdecl _expand_base( void *pMem, size_t nNewSize, int nBlockUse ) +{ + Assert( 0 ); + return NULL; +} + +// crt +void * __cdecl _malloc_crt(size_t size) +{ + return AllocUnattributed( size ); +} + +void * __cdecl _calloc_crt(size_t count, size_t size) +{ + return _calloc_base( count, size ); +} + +void * __cdecl _realloc_crt(void *ptr, size_t size) +{ + return _realloc_base( ptr, size ); +} + +void * __cdecl _recalloc_crt(void *ptr, size_t count, size_t size) +{ + return _recalloc_base( ptr, size * count ); +} + +ALLOC_CALL void * __cdecl _recalloc ( void * memblock, size_t count, size_t size ) +{ + void *pMem = ReallocUnattributed( memblock, size * count ); + if ( !memblock ) + { + memset( pMem, 0, size * count ); + } + return pMem; +} + +size_t _msize_base( void *pMem ) +{ + return g_pMemAlloc->GetSize(pMem); +} + +size_t _msize( void *pMem ) +{ + return _msize_base(pMem); +} + +size_t msize( void *pMem ) +{ + return g_pMemAlloc->GetSize(pMem); +} + +void *__cdecl _heap_alloc( size_t nSize ) +{ + return AllocUnattributed( nSize ); +} + +void *__cdecl _nh_malloc( size_t nSize, int ) +{ + return AllocUnattributed( nSize ); +} + +void *__cdecl _expand( void *pMem, size_t nSize ) +{ + Assert( 0 ); + return NULL; +} + +unsigned int _amblksiz = 16; //BYTES_PER_PARA; + +#if _MSC_VER >= 1400 +HANDLE _crtheap = (HANDLE)1; // PatM Can't be 0 or CRT pukes +int __active_heap = 1; +#endif // _MSC_VER >= 1400 + +size_t __cdecl _get_sbh_threshold( void ) +{ + return 0; +} + +int __cdecl _set_sbh_threshold( size_t ) +{ + return 0; +} + +int _heapchk() +{ + return g_pMemAlloc->heapchk(); +} + +int _heapmin() +{ + return 1; +} + +int __cdecl _heapadd( void *, size_t ) +{ + return 0; +} + +int __cdecl _heapset( unsigned int ) +{ + return 0; +} + +size_t __cdecl _heapused( size_t *, size_t * ) +{ + return 0; +} + +#ifdef _WIN32 +int __cdecl _heapwalk( _HEAPINFO * ) +{ + return 0; +} +#endif + +} // end extern "C" + + +//----------------------------------------------------------------------------- +// Debugging functions that we're going to override to call our allocator +// NOTE: These have to be here for release + debug builds in case we +// link to a debug static lib!!! +//----------------------------------------------------------------------------- + +extern "C" +{ + +void *malloc_db( size_t nSize, const char *pFileName, int nLine ) +{ + return g_pMemAlloc->Alloc(nSize, pFileName, nLine); +} + +void free_db( void *pMem, const char *pFileName, int nLine ) +{ + g_pMemAlloc->Free(pMem, pFileName, nLine); +} + +void *realloc_db( void *pMem, size_t nSize, const char *pFileName, int nLine ) +{ + return g_pMemAlloc->Realloc(pMem, nSize, pFileName, nLine); +} + +} // end extern "C" + +//----------------------------------------------------------------------------- +// These methods are standard MSVC heap initialization + shutdown methods +//----------------------------------------------------------------------------- +extern "C" +{ + +#if !defined( _X360 ) + int __cdecl _heap_init() + { + return g_pMemAlloc != NULL; + } + + void __cdecl _heap_term() + { + } +#endif + +} +#endif + + +//----------------------------------------------------------------------------- +// Prevents us from using an inappropriate new or delete method, +// ensures they are here even when linking against debug or release static libs +//----------------------------------------------------------------------------- +#ifndef NO_MEMOVERRIDE_NEW_DELETE +#ifdef OSX +void *__cdecl operator new( size_t nSize ) throw (std::bad_alloc) +#else +void *__cdecl operator new( size_t nSize ) +#endif +{ + return AllocUnattributed( nSize ); +} + +void *__cdecl operator new( size_t nSize, int nBlockUse, const char *pFileName, int nLine ) +{ + return g_pMemAlloc->Alloc(nSize, pFileName, nLine); +} + +#ifdef OSX +void __cdecl operator delete( void *pMem ) throw() +#else +void __cdecl operator delete( void *pMem ) +#endif +{ + g_pMemAlloc->Free( pMem ); +} + +#ifdef OSX +void operator delete(void*pMem, std::size_t) +#else +void operator delete(void*pMem, std::size_t) throw() +#endif +{ + g_pMemAlloc->Free( pMem ); +} + +#ifdef OSX +void *__cdecl operator new[]( size_t nSize ) throw (std::bad_alloc) +#else +void *__cdecl operator new[]( size_t nSize ) +#endif +{ + return AllocUnattributed( nSize ); +} + +void *__cdecl operator new[] ( size_t nSize, int nBlockUse, const char *pFileName, int nLine ) +{ + return g_pMemAlloc->Alloc(nSize, pFileName, nLine); +} + +#ifdef OSX +void __cdecl operator delete[]( void *pMem ) throw() +#else +void __cdecl operator delete[]( void *pMem ) +#endif +{ + g_pMemAlloc->Free( pMem ); +} +#endif + + +//----------------------------------------------------------------------------- +// Override some debugging allocation methods in MSVC +// NOTE: These have to be here for release + debug builds in case we +// link to a debug static lib!!! +//----------------------------------------------------------------------------- +#ifndef _STATIC_LINKED +#ifdef _WIN32 + +// This here just hides the internal file names, etc of allocations +// made in the c runtime library +#define CRT_INTERNAL_FILE_NAME "C-runtime internal" + +class CAttibCRT +{ +public: + CAttibCRT(int nBlockUse) : m_nBlockUse(nBlockUse) + { + if (m_nBlockUse == _CRT_BLOCK) + { + g_pMemAlloc->PushAllocDbgInfo(CRT_INTERNAL_FILE_NAME, 0); + } + } + + ~CAttibCRT() + { + if (m_nBlockUse == _CRT_BLOCK) + { + g_pMemAlloc->PopAllocDbgInfo(); + } + } + +private: + int m_nBlockUse; +}; + + +#define AttribIfCrt() CAttibCRT _attrib(nBlockUse) +#elif defined(POSIX) +#define AttribIfCrt() +#endif // _WIN32 + + +extern "C" +{ + +void *__cdecl _nh_malloc_dbg( size_t nSize, int nFlag, int nBlockUse, + const char *pFileName, int nLine ) +{ + AttribIfCrt(); + return g_pMemAlloc->Alloc(nSize, pFileName, nLine); +} + +void *__cdecl _malloc_dbg( size_t nSize, int nBlockUse, + const char *pFileName, int nLine ) +{ + AttribIfCrt(); + return g_pMemAlloc->Alloc(nSize, pFileName, nLine); +} + +#if defined( _X360 ) +void *__cdecl _calloc_dbg_impl( size_t nNum, size_t nSize, int nBlockUse, + const char * szFileName, int nLine, int * errno_tmp ) +{ + return _calloc_dbg( nNum, nSize, nBlockUse, szFileName, nLine ); +} +#endif + +void *__cdecl _calloc_dbg( size_t nNum, size_t nSize, int nBlockUse, + const char *pFileName, int nLine ) +{ + AttribIfCrt(); + void *pMem = g_pMemAlloc->Alloc(nSize * nNum, pFileName, nLine); + memset(pMem, 0, nSize * nNum); + return pMem; +} + +void *__cdecl _calloc_dbg_impl( size_t nNum, size_t nSize, int nBlockUse, + const char * szFileName, int nLine, int * errno_tmp ) +{ + return _calloc_dbg( nNum, nSize, nBlockUse, szFileName, nLine ); +} + +void *__cdecl _realloc_dbg( void *pMem, size_t nNewSize, int nBlockUse, + const char *pFileName, int nLine ) +{ + AttribIfCrt(); + return g_pMemAlloc->Realloc(pMem, nNewSize, pFileName, nLine); +} + +void *__cdecl _expand_dbg( void *pMem, size_t nNewSize, int nBlockUse, + const char *pFileName, int nLine ) +{ + Assert( 0 ); + return NULL; +} + +void __cdecl _free_dbg( void *pMem, int nBlockUse ) +{ + AttribIfCrt(); + g_pMemAlloc->Free(pMem); +} + +size_t __cdecl _msize_dbg( void *pMem, int nBlockUse ) +{ +#ifdef _WIN32 + return _msize(pMem); +#elif POSIX + Assert( "_msize_dbg unsupported" ); + return 0; +#endif +} + + +#ifdef _WIN32 + +#if defined(_DEBUG) && _MSC_VER >= 1300 +// aligned base +ALLOC_CALL void *__cdecl _aligned_malloc_base( size_t size, size_t align ) +{ + return MemAlloc_AllocAligned( size, align ); +} + +inline void *MemAlloc_Unalign( void *pMemBlock ) +{ + unsigned *pAlloc = (unsigned *)pMemBlock; + + // pAlloc points to the pointer to starting of the memory block + pAlloc = (unsigned *)(((size_t)pAlloc & ~(sizeof( void * ) - 1)) - sizeof( void * )); + + // pAlloc is the pointer to the start of memory block + return *((unsigned **)pAlloc); +} + +ALLOC_CALL void *__cdecl _aligned_realloc_base( void *ptr, size_t size, size_t align ) +{ + if ( ptr && !size ) + { + MemAlloc_FreeAligned( ptr ); + return NULL; + } + + void *pNew = MemAlloc_AllocAligned( size, align ); + if ( ptr ) + { + void *ptrUnaligned = MemAlloc_Unalign( ptr ); + size_t oldSize = g_pMemAlloc->GetSize( ptrUnaligned ); + size_t oldOffset = (uintp)ptr - (uintp)ptrUnaligned; + size_t copySize = oldSize - oldOffset; + if ( copySize > size ) + copySize = size; + memcpy( pNew, ptr, copySize ); + MemAlloc_FreeAligned( ptr ); + } + return pNew; +} + +ALLOC_CALL void *__cdecl _aligned_recalloc_base( void *ptr, size_t size, size_t align ) +{ + Error( "Unsupported function\n" ); + return NULL; +} + +FREE_CALL void __cdecl _aligned_free_base( void *ptr ) +{ + MemAlloc_FreeAligned( ptr ); +} + +// aligned +ALLOC_CALL void * __cdecl _aligned_malloc( size_t size, size_t align ) +{ + return _aligned_malloc_base(size, align); +} + +ALLOC_CALL void *__cdecl _aligned_realloc(void *memblock, size_t size, size_t align) +{ + return _aligned_realloc_base(memblock, size, align); +} + +ALLOC_CALL void * __cdecl _aligned_recalloc( void * memblock, size_t count, size_t size, size_t align ) +{ + return _aligned_recalloc_base(memblock, count * size, align); +} + +FREE_CALL void __cdecl _aligned_free( void *memblock ) +{ + _aligned_free_base(memblock); +} + +// aligned offset base +ALLOC_CALL void * __cdecl _aligned_offset_malloc_base( size_t size, size_t align, size_t offset ) +{ + Assert( IsPC() || 0 ); + return NULL; +} + +ALLOC_CALL void * __cdecl _aligned_offset_realloc_base( void * memblock, size_t size, size_t align, size_t offset) +{ + Assert( IsPC() || 0 ); + return NULL; +} + +ALLOC_CALL void * __cdecl _aligned_offset_recalloc_base( void * memblock, size_t size, size_t align, size_t offset) +{ + Assert( IsPC() || 0 ); + return NULL; +} + +// aligned offset +ALLOC_CALL void *__cdecl _aligned_offset_malloc(size_t size, size_t align, size_t offset) +{ + return _aligned_offset_malloc_base( size, align, offset ); +} + +ALLOC_CALL void *__cdecl _aligned_offset_realloc(void *memblock, size_t size, size_t align, size_t offset) +{ + return _aligned_offset_realloc_base( memblock, size, align, offset ); +} + +ALLOC_CALL void * __cdecl _aligned_offset_recalloc( void * memblock, size_t count, size_t size, size_t align, size_t offset ) +{ + return _aligned_offset_recalloc_base( memblock, count * size, align, offset ); +} + +#endif // _MSC_VER >= 1400 + +#endif + +} // end extern "C" + + +//----------------------------------------------------------------------------- +// Override some the _CRT debugging allocation methods in MSVC +//----------------------------------------------------------------------------- +#ifdef _WIN32 + +extern "C" +{ + +int _CrtDumpMemoryLeaks(void) +{ + return 0; +} + +_CRT_DUMP_CLIENT _CrtSetDumpClient( _CRT_DUMP_CLIENT dumpClient ) +{ + return NULL; +} + +int _CrtSetDbgFlag( int nNewFlag ) +{ + return g_pMemAlloc->CrtSetDbgFlag( nNewFlag ); +} + +// 64-bit port. +#define AFNAME(var) __p_##var +#define AFRET(var) &var +#if defined(_crtDbgFlag) +#undef _crtDbgFlag +#endif +#if defined(_crtBreakAlloc) +#undef _crtBreakAlloc +#endif + +int _crtDbgFlag = _CRTDBG_ALLOC_MEM_DF; +int* AFNAME(_crtDbgFlag)(void) +{ + return AFRET(_crtDbgFlag); +} + +long _crtBreakAlloc; /* Break on this allocation */ +long* AFNAME(_crtBreakAlloc) (void) +{ + return AFRET(_crtBreakAlloc); +} + +void __cdecl _CrtSetDbgBlockType( void *pMem, int nBlockUse ) +{ + DebuggerBreak(); +} + +_CRT_ALLOC_HOOK __cdecl _CrtSetAllocHook( _CRT_ALLOC_HOOK pfnNewHook ) +{ + DebuggerBreak(); + return NULL; +} + +long __cdecl _CrtSetBreakAlloc( long lNewBreakAlloc ) +{ + return g_pMemAlloc->CrtSetBreakAlloc( lNewBreakAlloc ); +} + +int __cdecl _CrtIsValidHeapPointer( const void *pMem ) +{ + return g_pMemAlloc->CrtIsValidHeapPointer( pMem ); +} + +int __cdecl _CrtIsValidPointer( const void *pMem, unsigned int size, int access ) +{ + return g_pMemAlloc->CrtIsValidPointer( pMem, size, access ); +} + +int __cdecl _CrtCheckMemory( void ) +{ + // FIXME: Remove this when we re-implement the heap + return g_pMemAlloc->CrtCheckMemory( ); +} + +int __cdecl _CrtIsMemoryBlock( const void *pMem, unsigned int nSize, + long *plRequestNumber, char **ppFileName, int *pnLine ) +{ + DebuggerBreak(); + return 1; +} + +int __cdecl _CrtMemDifference( _CrtMemState *pState, const _CrtMemState * oldState, const _CrtMemState * newState ) +{ + DebuggerBreak(); + return FALSE; +} + +void __cdecl _CrtMemDumpStatistics( const _CrtMemState *pState ) +{ + DebuggerBreak(); +} + +void __cdecl _CrtMemCheckpoint( _CrtMemState *pState ) +{ + // FIXME: Remove this when we re-implement the heap + g_pMemAlloc->CrtMemCheckpoint( pState ); +} + +void __cdecl _CrtMemDumpAllObjectsSince( const _CrtMemState *pState ) +{ + DebuggerBreak(); +} + +void __cdecl _CrtDoForAllClientObjects( void (*pfn)(void *, void *), void * pContext ) +{ + DebuggerBreak(); +} + + +//----------------------------------------------------------------------------- +// Methods in dbgrpt.cpp +//----------------------------------------------------------------------------- +long _crtAssertBusy = -1; + +int __cdecl _CrtSetReportMode( int nReportType, int nReportMode ) +{ + return g_pMemAlloc->CrtSetReportMode( nReportType, nReportMode ); +} + +_HFILE __cdecl _CrtSetReportFile( int nRptType, _HFILE hFile ) +{ + return (_HFILE)g_pMemAlloc->CrtSetReportFile( nRptType, hFile ); +} + +_CRT_REPORT_HOOK __cdecl _CrtSetReportHook( _CRT_REPORT_HOOK pfnNewHook ) +{ + return (_CRT_REPORT_HOOK)g_pMemAlloc->CrtSetReportHook( pfnNewHook ); +} + +int __cdecl _CrtDbgReport( int nRptType, const char * szFile, + int nLine, const char * szModule, const char * szFormat, ... ) +{ + static char output[1024]; + va_list args; + if ( szFormat ) + { + va_start( args, szFormat ); + _vsnprintf( output, sizeof( output )-1, szFormat, args ); + va_end( args ); + } + else + { + output[0] = 0; + } + + return g_pMemAlloc->CrtDbgReport( nRptType, szFile, nLine, szModule, output ); +} + +#if _MSC_VER >= 1400 + +// Configure VS so that it will record crash dumps on pure-call violations +// and invalid parameter handlers. +// If you manage to call a pure-virtual function (easily done if you indirectly +// call a pure-virtual function from the base-class constructor or destructor) +// or if you invoke the invalid parameter handler (printf(NULL); is one way) +// then no crash dump will be created. +// This crash redirects the handlers for these two events so that crash dumps +// are created. +// +// The ErrorHandlerRegistrar object must be in memoverride.cpp so that it will +// be placed in every DLL and EXE. This is required because each DLL and EXE +// gets its own copy of the C run-time and these overrides are set on a per-CRT +// basis. + +/* +// This sample code will cause pure-call and invalid_parameter violations and +// was used for testing: +class Base +{ +public: + virtual void PureFunction() = 0; + + Base() + { + NonPureFunction(); + } + + void NonPureFunction() + { + PureFunction(); + } +}; + +class Derived : public Base +{ +public: + void PureFunction() OVERRIDE + { + } +}; + +void PureCallViolation() +{ + Derived derived; +} + +void InvalidParameterViolation() +{ + printf( NULL ); +} +*/ + +#include <stdlib.h> +#include "minidump.h" + +// Disable compiler optimizations. If we don't do this then VC++ generates code +// that confuses the Visual Studio debugger and causes it to display completely +// random call stacks. That makes the minidumps excruciatingly hard to understand. +#pragma optimize("", off) + +// Write a minidump file, unless running under the debugger in which case break +// into the debugger. +// The "int dummy" parameter is so that the callers can be unique so that the +// linker won't use its /opt:icf optimization to collapse them together. This +// makes reading the call stack easier. +void __cdecl WriteMiniDumpOrBreak( int dummy, const char *pchName ) +{ + if ( Plat_IsInDebugSession() ) + { + __debugbreak(); + // Continue at your peril... + } + else + { + WriteMiniDump( pchName ); + // Call Plat_ExitProcess so we don't continue in a bad state. + TerminateProcess(GetCurrentProcess(), 0); + } +} + +void __cdecl VPureCall() +{ + WriteMiniDumpOrBreak( 0, "PureClass" ); +} + +void VInvalidParameterHandler(const wchar_t* expression, + const wchar_t* function, + const wchar_t* file, + unsigned int line, + uintptr_t pReserved) +{ + WriteMiniDumpOrBreak( 1, "InvalidParameterHandler" ); +} + +// Restore compiler optimizations. +#pragma optimize("", on) + +// Helper class for registering error callbacks. See above for details. +class ErrorHandlerRegistrar +{ +public: + ErrorHandlerRegistrar(); +} s_ErrorHandlerRegistration; + +ErrorHandlerRegistrar::ErrorHandlerRegistrar() +{ + _set_purecall_handler( VPureCall ); + _set_invalid_parameter_handler( VInvalidParameterHandler ); +} + +#if defined( _DEBUG ) + +// wrapper which passes no debug info; not available in debug +#ifndef SUPPRESS_INVALID_PARAMETER_NO_INFO +void __cdecl _invalid_parameter_noinfo(void) +{ + Assert(0); +} +#endif + +#endif /* defined( _DEBUG ) */ + +#if defined( _DEBUG ) || defined( USE_MEM_DEBUG ) + +int __cdecl __crtMessageWindowW( int nRptType, const wchar_t * szFile, const wchar_t * szLine, + const wchar_t * szModule, const wchar_t * szUserMessage ) +{ + Assert(0); + return 0; +} + +int __cdecl _CrtDbgReportV( int nRptType, const wchar_t *szFile, int nLine, + const wchar_t *szModule, const wchar_t *szFormat, va_list arglist ) +{ + Assert(0); + return 0; +} + +int __cdecl _CrtDbgReportW( int nRptType, const wchar_t *szFile, int nLine, + const wchar_t *szModule, const wchar_t *szFormat, ...) +{ + Assert(0); + return 0; +} + +#if _MSC_VER >= 1900 +int __cdecl _VCrtDbgReportA( int nRptType, void* ReturnAddress, const char * szFile, int nLine, + const char * szModule, const char * szFormat, va_list arglist ) +{ + Assert(0); + return 0; +} +#else +int __cdecl _VCrtDbgReportA( int nRptType, const wchar_t * szFile, int nLine, + const wchar_t * szModule, const wchar_t * szFormat, va_list arglist ) +{ + Assert( 0 ); + return 0; +} +#endif + +int __cdecl _CrtSetReportHook2( int mode, _CRT_REPORT_HOOK pfnNewHook ) +{ + _CrtSetReportHook( pfnNewHook ); + return 0; +} + + +#endif /* defined( _DEBUG ) || defined( USE_MEM_DEBUG ) */ + +extern "C" int __crtDebugCheckCount = FALSE; + +extern "C" int __cdecl _CrtSetCheckCount( int fCheckCount ) +{ + int oldCheckCount = __crtDebugCheckCount; + return oldCheckCount; +} + +extern "C" int __cdecl _CrtGetCheckCount( void ) +{ + return __crtDebugCheckCount; +} + +// aligned offset debug +extern "C" void * __cdecl _aligned_offset_recalloc_dbg( void * memblock, size_t count, size_t size, size_t align, size_t offset, const char * f_name, int line_n ) +{ + Assert( IsPC() || 0 ); + void *pMem = ReallocUnattributed( memblock, size * count ); + if ( !memblock ) + { + memset( pMem, 0, size * count ); + } + return pMem; +} + +extern "C" void * __cdecl _aligned_recalloc_dbg( void *memblock, size_t count, size_t size, size_t align, const char * f_name, int line_n ) +{ + return _aligned_offset_recalloc_dbg(memblock, count, size, align, 0, f_name, line_n); +} + +extern "C" void * __cdecl _recalloc_dbg ( void * memblock, size_t count, size_t size, int nBlockUse, const char * szFileName, int nLine ) +{ + return _aligned_offset_recalloc_dbg(memblock, count, size, 0, 0, szFileName, nLine); +} + +_CRT_REPORT_HOOK __cdecl _CrtGetReportHook( void ) +{ + return NULL; +} + +#endif +int __cdecl _CrtReportBlockType(const void * pUserData) +{ + return 0; +} + + +} // end extern "C" +#endif // _WIN32 + +// Most files include this file, so when it's used it adds an extra .ValveDbg section, +// to help identify debug binaries. +#ifdef _WIN32 + #ifndef NDEBUG // _DEBUG + #pragma data_seg("ValveDBG") + volatile const char* DBG = "*** DEBUG STUB ***"; + #endif +#endif + +#endif + +// Extras added prevent dbgheap.obj from being included - DAL +#ifdef _WIN32 + +extern "C" +{ +size_t __crtDebugFillThreshold = 0; + +extern "C" void * __cdecl _heap_alloc_base (size_t size) { + assert(0); + return NULL; +} + + +void * __cdecl _heap_alloc_dbg( size_t nSize, int nBlockUse, const char * szFileName, int nLine) +{ + return _heap_alloc(nSize); +} + +// 64-bit +#ifdef _WIN64 +static void * __cdecl realloc_help( void * pUserData, size_t * pnNewSize, int nBlockUse,const char * szFileName, + int nLine, int fRealloc ) +{ + assert(0); // Shouldn't be needed + return NULL; +} +#else +static void * __cdecl realloc_help( void * pUserData, size_t nNewSize, int nBlockUse, const char * szFileName, + int nLine, int fRealloc) +{ + assert(0); // Shouldn't be needed + return NULL; +} +#endif + +void __cdecl _free_nolock( void * pUserData) +{ + // I don't think the second param is used in memoverride + _free_dbg(pUserData, 0); +} + +void __cdecl _free_dbg_nolock( void * pUserData, int nBlockUse) +{ + _free_dbg(pUserData, 0); +} + +_CRT_ALLOC_HOOK __cdecl _CrtGetAllocHook ( void) +{ + assert(0); + return NULL; +} + +static int __cdecl CheckBytes( unsigned char * pb, unsigned char bCheck, size_t nSize) +{ + int bOkay = TRUE; + return bOkay; +} + + +_CRT_DUMP_CLIENT __cdecl _CrtGetDumpClient ( void) +{ + assert(0); + return NULL; +} + +#if _MSC_VER >= 1400 +static void __cdecl _printMemBlockData( _locale_t plocinfo, _CrtMemBlockHeader * pHead) +{ +} + +static void __cdecl _CrtMemDumpAllObjectsSince_stat( const _CrtMemState * state, _locale_t plocinfo) +{ +} +#endif +void * __cdecl _aligned_malloc_dbg( size_t size, size_t align, const char * f_name, int line_n) +{ + return _aligned_malloc(size, align); +} + +void * __cdecl _aligned_realloc_dbg( void *memblock, size_t size, size_t align, + const char * f_name, int line_n) +{ + return _aligned_realloc(memblock, size, align); +} + +void * __cdecl _aligned_offset_malloc_dbg( size_t size, size_t align, size_t offset, + const char * f_name, int line_n) +{ + return _aligned_offset_malloc(size, align, offset); +} + +void * __cdecl _aligned_offset_realloc_dbg( void * memblock, size_t size, size_t align, + size_t offset, const char * f_name, int line_n) +{ + return _aligned_offset_realloc(memblock, size, align, offset); +} + +void __cdecl _aligned_free_dbg( void * memblock) +{ + _aligned_free(memblock); +} + +#if _MSC_VER < 1900 +size_t __cdecl _CrtSetDebugFillThreshold( size_t _NewDebugFillThreshold) +{ + assert(0); + return 0; +} + +//=========================================== +// NEW!!! 64-bit + +char * __cdecl _strdup ( const char * string ) +{ + int nSize = (int)strlen(string) + 1; + // Check for integer underflow. + if ( nSize <= 0 ) + return NULL; + char *pCopy = (char*)AllocUnattributed( nSize ); + if ( pCopy ) + memcpy( pCopy, string, nSize ); + return pCopy; +} +#endif + +#if 0 +_TSCHAR * __cdecl _tfullpath_dbg ( _TSCHAR *UserBuf, const _TSCHAR *path, size_t maxlen, int nBlockUse, const char * szFileName, int nLine ) +{ + Assert(0); + return NULL; +} + +_TSCHAR * __cdecl _tfullpath ( _TSCHAR *UserBuf, const _TSCHAR *path, size_t maxlen ) +{ + Assert(0); + return NULL; +} + +_TSCHAR * __cdecl _tgetdcwd_lk_dbg ( int drive, _TSCHAR *pnbuf, int maxlen, int nBlockUse, const char * szFileName, int nLine ) +{ + Assert(0); + return NULL; +} + +_TSCHAR * __cdecl _tgetdcwd_nolock ( int drive, _TSCHAR *pnbuf, int maxlen ) +{ + Assert(0); + return NULL; +} + +errno_t __cdecl _tdupenv_s_helper ( _TSCHAR **pBuffer, size_t *pBufferSizeInTChars, const _TSCHAR *varname, int nBlockUse, const char * szFileName, int nLine ) +{ + Assert(0); + return 0; +} + +errno_t __cdecl _tdupenv_s_helper ( _TSCHAR **pBuffer, size_t *pBufferSizeInTChars, const _TSCHAR *varname ) +{ + Assert(0); + return 0; +} + +_TSCHAR * __cdecl _ttempnam_dbg ( const _TSCHAR *dir, const _TSCHAR *pfx, int nBlockUse, const char * szFileName, int nLine ) +{ + Assert(0); + return 0; +} + +_TSCHAR * __cdecl _ttempnam ( const _TSCHAR *dir, const _TSCHAR *pfx ) +{ + Assert(0); + return 0; +} + +wchar_t * __cdecl _wcsdup_dbg ( const wchar_t * string, int nBlockUse, const char * szFileName, int nLine ) +{ + Assert(0); + return 0; +} + +wchar_t * __cdecl _wcsdup ( const wchar_t * string ) +{ + Assert(0); + return 0; +} +#endif +} // end extern "C" + +#if _MSC_VER >= 1400 + +//----------------------------------------------------------------------------- +// XBox Memory Allocator Override +//----------------------------------------------------------------------------- +#if defined( _X360 ) +#if defined( _DEBUG ) || defined( USE_MEM_DEBUG ) +#include "utlmap.h" + +MEMALLOC_DEFINE_EXTERNAL_TRACKING( XMem ); + +CThreadFastMutex g_XMemAllocMutex; + +void XMemAlloc_RegisterAllocation( void *p, DWORD dwAllocAttributes ) +{ + if ( !g_pMemAlloc ) + { + // core xallocs cannot be journaled until system is ready + return; + } + + AUTO_LOCK_FM( g_XMemAllocMutex ); + int size = XMemSize( p, dwAllocAttributes ); + MemAlloc_RegisterExternalAllocation( XMem, p, size ); +} + +void XMemAlloc_RegisterDeallocation( void *p, DWORD dwAllocAttributes ) +{ + if ( !g_pMemAlloc ) + { + // core xallocs cannot be journaled until system is ready + return; + } + + AUTO_LOCK_FM( g_XMemAllocMutex ); + int size = XMemSize( p, dwAllocAttributes ); + MemAlloc_RegisterExternalDeallocation( XMem, p, size ); +} + +#else + +#define XMemAlloc_RegisterAllocation( p, a ) ((void)0) +#define XMemAlloc_RegisterDeallocation( p, a ) ((void)0) + +#endif + +//----------------------------------------------------------------------------- +// XMemAlloc +// +// XBox Memory Allocator Override +//----------------------------------------------------------------------------- +LPVOID WINAPI XMemAlloc( SIZE_T dwSize, DWORD dwAllocAttributes ) +{ + LPVOID ptr; + XALLOC_ATTRIBUTES *pAttribs = (XALLOC_ATTRIBUTES *)&dwAllocAttributes; + bool bPhysical = ( pAttribs->dwMemoryType == XALLOC_MEMTYPE_PHYSICAL ); + + if ( !bPhysical && !pAttribs->dwHeapTracksAttributes && pAttribs->dwAllocatorId != eXALLOCAllocatorId_XUI ) + { + MEM_ALLOC_CREDIT(); + switch ( pAttribs->dwAlignment ) + { + case XALLOC_ALIGNMENT_4: + ptr = g_pMemAlloc->Alloc( dwSize ); + break; + case XALLOC_ALIGNMENT_8: + ptr = MemAlloc_AllocAligned( dwSize, 8 ); + break; + case XALLOC_ALIGNMENT_DEFAULT: + case XALLOC_ALIGNMENT_16: + default: + ptr = MemAlloc_AllocAligned( dwSize, 16 ); + break; + } + if ( pAttribs->dwZeroInitialize != 0 ) + { + memset( ptr, 0, XMemSize( ptr, dwAllocAttributes ) ); + } + return ptr; + } + + ptr = XMemAllocDefault( dwSize, dwAllocAttributes ); + if ( ptr ) + { + XMemAlloc_RegisterAllocation( ptr, dwAllocAttributes ); + } + + return ptr; +} + +//----------------------------------------------------------------------------- +// XMemFree +// +// XBox Memory Allocator Override +//----------------------------------------------------------------------------- +VOID WINAPI XMemFree( PVOID pAddress, DWORD dwAllocAttributes ) +{ + if ( !pAddress ) + { + return; + } + + XALLOC_ATTRIBUTES *pAttribs = (XALLOC_ATTRIBUTES *)&dwAllocAttributes; + bool bPhysical = ( pAttribs->dwMemoryType == XALLOC_MEMTYPE_PHYSICAL ); + + if ( !bPhysical && !pAttribs->dwHeapTracksAttributes && pAttribs->dwAllocatorId != eXALLOCAllocatorId_XUI ) + { + switch ( pAttribs->dwAlignment ) + { + case XALLOC_ALIGNMENT_4: + return g_pMemAlloc->Free( pAddress ); + default: + return MemAlloc_FreeAligned( pAddress ); + } + return; + } + + XMemAlloc_RegisterDeallocation( pAddress, dwAllocAttributes ); + + XMemFreeDefault( pAddress, dwAllocAttributes ); +} + +//----------------------------------------------------------------------------- +// XMemSize +// +// XBox Memory Allocator Override +//----------------------------------------------------------------------------- +SIZE_T WINAPI XMemSize( PVOID pAddress, DWORD dwAllocAttributes ) +{ + XALLOC_ATTRIBUTES *pAttribs = (XALLOC_ATTRIBUTES *)&dwAllocAttributes; + bool bPhysical = ( pAttribs->dwMemoryType == XALLOC_MEMTYPE_PHYSICAL ); + + if ( !bPhysical && !pAttribs->dwHeapTracksAttributes && pAttribs->dwAllocatorId != eXALLOCAllocatorId_XUI ) + { + switch ( pAttribs->dwAlignment ) + { + case XALLOC_ALIGNMENT_4: + return g_pMemAlloc->GetSize( pAddress ); + default: + return MemAlloc_GetSizeAligned( pAddress ); + } + } + + return XMemSizeDefault( pAddress, dwAllocAttributes ); +} +#endif + +#pragma warning(push) +#pragma warning(disable: 4483) +#if _MSC_FULL_VER >= 140050415 +#define _NATIVE_STARTUP_NAMESPACE __identifier("<CrtImplementationDetails>") +#else /* _MSC_FULL_VER >= 140050415 */ +#define _NATIVE_STARTUP_NAMESPACE __CrtImplementationDetails +#endif /* _MSC_FULL_VER >= 140050415 */ + +namespace _NATIVE_STARTUP_NAMESPACE +{ + class NativeDll + { + private: + static const unsigned int ProcessDetach = 0; + static const unsigned int ProcessAttach = 1; + static const unsigned int ThreadAttach = 2; + static const unsigned int ThreadDetach = 3; + static const unsigned int ProcessVerifier = 4; + + public: + + inline static bool IsInDllMain() + { + return false; + } + + inline static bool IsInProcessAttach() + { + return false; + } + + inline static bool IsInProcessDetach() + { + return false; + } + + inline static bool IsInVcclrit() + { + return false; + } + + inline static bool IsSafeForManagedCode() + { + if (!IsInDllMain()) + { + return true; + } + + if (IsInVcclrit()) + { + return true; + } + + return !IsInProcessAttach() && !IsInProcessDetach(); + } + }; +} +#pragma warning(pop) + +#endif // _MSC_VER >= 1400 + +#endif // !STEAM && !NO_MALLOC_OVERRIDE + +#endif // _WIN32 diff --git a/public/tier0/minidump.h b/public/tier0/minidump.h new file mode 100644 index 0000000..e650725 --- /dev/null +++ b/public/tier0/minidump.h @@ -0,0 +1,104 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +//=============================================================================// + +#ifndef MINIDUMP_H +#define MINIDUMP_H +#ifdef _WIN32 +#pragma once +#endif + +#include "tier0/platform.h" + +// Set prefix to use for minidump files. If you don't set one, it is defaulted for you, +// using the current module name +PLATFORM_INTERFACE void SetMinidumpFilenamePrefix( const char *pszPrefix ); + +// Set comment to put into minidump file upon next call of WriteMiniDump. (Most common use is the assert text.) +PLATFORM_INTERFACE void SetMinidumpComment( const char *pszComment ); + +// writes out a minidump of the current stack trace with a unique filename +PLATFORM_INTERFACE void WriteMiniDump( const char *pszFilenameSuffix = NULL ); + +typedef void (*FnWMain)( int , tchar *[] ); +typedef void (*FnVoidPtrFn)( void * ); + +#if defined(_WIN32) && !defined(_X360) + +// calls the passed in function pointer and catches any exceptions/crashes thrown by it, and writes a minidump +// use from wmain() to protect the whole program +typedef void (*FnWMain)( int , tchar *[] ); +typedef int (*FnWMainIntRet)( int , tchar *[] ); +typedef void (*FnVoidPtrFn)( void * ); + +enum ECatchAndWriteMinidumpAction +{ + k_ECatchAndWriteMiniDumpAbort = 0, + k_ECatchAndWriteMiniDumpReThrow = 1, + k_ECatchAndWriteMiniDumpIgnore = 2, +}; + +PLATFORM_INTERFACE void CatchAndWriteMiniDump( FnWMain pfn, int argc, tchar *argv[] ); // action = Abort +PLATFORM_INTERFACE void CatchAndWriteMiniDumpForVoidPtrFn( FnVoidPtrFn pfn, void *pv, bool bExitQuietly ); // action = abort if bExitQuietly, Rethrow otherwise + +PLATFORM_INTERFACE void CatchAndWriteMiniDumpEx( FnWMain pfn, int argc, tchar *argv[], ECatchAndWriteMinidumpAction eAction ); +PLATFORM_INTERFACE int CatchAndWriteMiniDumpExReturnsInt( FnWMainIntRet pfn, int argc, tchar *argv[], ECatchAndWriteMinidumpAction eAction ); +PLATFORM_INTERFACE void CatchAndWriteMiniDumpExForVoidPtrFn( FnVoidPtrFn pfn, void *pv, ECatchAndWriteMinidumpAction eAction ); + +// Let's not include this. We'll use forwards instead. +//#include <dbghelp.h> +struct _EXCEPTION_POINTERS; + +// Replaces the current function pointer with the one passed in. +// Returns the previously-set function. +// The function is called internally by WriteMiniDump() and CatchAndWriteMiniDump() +// The default is the built-in function that uses DbgHlp.dll's MiniDumpWriteDump function +typedef void (*FnMiniDump)( unsigned int uStructuredExceptionCode, _EXCEPTION_POINTERS * pExceptionInfo, const char *pszFilenameSuffix ); +PLATFORM_INTERFACE FnMiniDump SetMiniDumpFunction( FnMiniDump pfn ); + +// Use this to write a minidump explicitly. +// Some of the tools choose to catch the minidump themselves instead of using CatchAndWriteMinidump +// so they can show their own dialog. +// +// ptchMinidumpFileNameBuffer if not-NULL should be a writable tchar buffer of length at +// least _MAX_PATH and on return will contain the name of the minidump file written. +// If ptchMinidumpFileNameBuffer is NULL the name of the minidump file written will not +// be available after the function returns. +// +PLATFORM_INTERFACE bool WriteMiniDumpUsingExceptionInfo( + unsigned int uStructuredExceptionCode, + _EXCEPTION_POINTERS * pExceptionInfo, + int /* MINIDUMP_TYPE */ minidumpType, + const char *pszFilenameSuffix = NULL, + tchar *ptchMinidumpFileNameBuffer = NULL + ); + +// Call this to enable a handler for unhandled exceptions. +PLATFORM_INTERFACE void MinidumpSetUnhandledExceptionFunction( FnMiniDump pfn ); + +// Call this to prevent crashes in kernel callbacks such as window procs from +// being silently swallowed. We should always call this at startup. +PLATFORM_INTERFACE void EnableCrashingOnCrashes(); + +#endif // defined(_WIN32) && !defined(_X360) + +// +// Minidump User Stream Info Comments. +// +// There currently is a single header string, and an array of 64 comment strings. +// MinidumpUserStreamInfoSetHeader() will set the single header string. +// MinidumpUserStreamInfoAppend() will round robin through and array and set the comment strings, overwriting old. +PLATFORM_INTERFACE void MinidumpUserStreamInfoSetHeader( const char *pFormat, ... ); +PLATFORM_INTERFACE void MinidumpUserStreamInfoAppend( const char *pFormat, ... ); + +// Retrieve the StreamInfo strings. +// Index 0: header string +// Index 1..: comment string +// Returns NULL when you've reached the end of the comment string array +// Empty strings ("\0") can be returned if comment hasn't been set +PLATFORM_INTERFACE const char *MinidumpUserStreamInfoGet( int Index ); + +#endif // MINIDUMP_H + diff --git a/public/tier0/platform.h b/public/tier0/platform.h new file mode 100644 index 0000000..6387139 --- /dev/null +++ b/public/tier0/platform.h @@ -0,0 +1,1691 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +// +//===========================================================================// + +#ifndef PLATFORM_H +#define PLATFORM_H + +#if defined(__x86_64__) || defined(_WIN64) +#define PLATFORM_64BITS 1 +#endif + +#if defined(__GCC__) || defined(__GNUC__) +#define COMPILER_GCC 1 +#endif + +#ifdef __clang__ +#define COMPILER_CLANG 1 +#endif + +#if defined( _X360 ) + #define NO_STEAM + #define NO_VOICE + // for the 360, the ppc platform and the rtos are tightly coupled + // setup the 360 environment here !once! for much less leaf module include wackiness + // these are critical order and purposely appear *before* anything else + #define _XBOX +#include <xtl.h> + #include <xaudio2.h> + #include <xbdm.h> +#include <Xgraphics.h> + #include <xui.h> + #include <pmcpbsetup.h> +#include <XMAHardwareAbstraction.h> + #undef _XBOX +#endif + +#define __STDC_LIMIT_MACROS +#include <stdint.h> + +#include "wchartypes.h" +#include "basetypes.h" +#include "tier0/valve_off.h" + +#ifdef _DEBUG +#if !defined( PLAT_COMPILE_TIME_ASSERT ) +#define PLAT_COMPILE_TIME_ASSERT( pred ) switch(0){case 0:case pred:;} +#endif +#else +#if !defined( PLAT_COMPILE_TIME_ASSERT ) +#define PLAT_COMPILE_TIME_ASSERT( pred ) +#endif +#endif + +#ifdef _WIN32 +#pragma once +#endif + +// feature enables +#define NEW_SOFTWARE_LIGHTING + +#ifdef POSIX +// need this for _alloca +#include <alloca.h> +#include <unistd.h> +#include <signal.h> +#include <time.h> +#endif + +#include <malloc.h> +#include <new> + +// need this for memset +#include <string.h> + +#include "tier0/valve_minmax_on.h" // GCC 4.2.2 headers screw up our min/max defs. + +#ifdef _RETAIL +#define IsRetail() true +#else +#define IsRetail() false +#endif + +#ifdef _DEBUG +#define IsRelease() false +#define IsDebug() true +#else +#define IsRelease() true +#define IsDebug() false +#endif + +// Deprecating, infavor of IsX360() which will revert to IsXbox() +// after confidence of xbox 1 code flush +#define IsXbox() false + +#ifdef _WIN32 + #define IsLinux() false + #define IsOSX() false + #define IsPosix() false + #define PLATFORM_WINDOWS 1 // Windows PC or Xbox 360 + #ifndef _X360 + #define IsWindows() true + #define IsPC() true + #define IsConsole() false + #define IsX360() false + #define IsPS3() false + #define IS_WINDOWS_PC + #define PLATFORM_WINDOWS_PC 1 // Windows PC + #ifdef _WIN64 + #define IsPlatformWindowsPC64() true + #define IsPlatformWindowsPC32() false + #define PLATFORM_WINDOWS_PC64 1 + #else + #define IsPlatformWindowsPC64() false + #define IsPlatformWindowsPC32() true + #define PLATFORM_WINDOWS_PC32 1 + #endif + #else + #define PLATFORM_X360 1 + #ifndef _CONSOLE + #define _CONSOLE + #endif + #define IsWindows() false + #define IsPC() false + #define IsConsole() true + #define IsX360() true + #define IsPS3() false + #endif + // Adding IsPlatformOpenGL() to help fix a bunch of code that was using IsPosix() to infer if the DX->GL translation layer was being used. + #if defined( DX_TO_GL_ABSTRACTION ) + #define IsPlatformOpenGL() true + #else + #define IsPlatformOpenGL() false + #endif +#elif defined(POSIX) + #define IsPC() true + #define IsWindows() false + #define IsConsole() false + #define IsX360() false + #define IsPS3() false + #if defined( LINUX ) + #define IsLinux() true + #else + #define IsLinux() false + #endif + + #if defined( OSX ) + #define IsOSX() true + #else + #define IsOSX() false + #endif + + #define IsPosix() true + #define IsPlatformOpenGL() true +#else + #error +#endif + +typedef unsigned char uint8; +typedef signed char int8; + +#if defined( _WIN32 ) + + typedef __int16 int16; + typedef unsigned __int16 uint16; + typedef __int32 int32; + typedef unsigned __int32 uint32; + typedef __int64 int64; + typedef unsigned __int64 uint64; + + #ifdef PLATFORM_64BITS + typedef __int64 intp; // intp is an integer that can accomodate a pointer + typedef unsigned __int64 uintp; // (ie, sizeof(intp) >= sizeof(int) && sizeof(intp) >= sizeof(void *) + #else + typedef __int32 intp; + typedef unsigned __int32 uintp; + #endif + + #if defined( _X360 ) + #ifdef __m128 + #undef __m128 + #endif + #define __m128 __vector4 + #endif + + // Use this to specify that a function is an override of a virtual function. + // This lets the compiler catch cases where you meant to override a virtual + // function but you accidentally changed the function signature and created + // an overloaded function. Usage in function declarations is like this: + // int GetData() const OVERRIDE; + #define OVERRIDE override + +#else // _WIN32 + + typedef short int16; + typedef unsigned short uint16; + typedef int int32; + typedef unsigned int uint32; + typedef long long int64; + typedef unsigned long long uint64; + #ifdef PLATFORM_64BITS + typedef long long intp; + typedef unsigned long long uintp; + #else + typedef int intp; + typedef unsigned int uintp; + #endif + typedef void *HWND; + + // Avoid redefinition warnings if a previous header defines this. + #undef OVERRIDE + #if __cplusplus >= 201103L + #define OVERRIDE override + #if defined(__clang__) + // warning: 'override' keyword is a C++11 extension [-Wc++11-extensions] + // Disabling this warning is less intrusive than enabling C++11 extensions + #pragma GCC diagnostic ignored "-Wc++11-extensions" + #endif + #else + #define OVERRIDE + #endif + +#endif // else _WIN32 + +//----------------------------------------------------------------------------- +// Set up platform type defines. +//----------------------------------------------------------------------------- +#if defined( PLATFORM_X360 ) || defined( _PS3 ) + #if !defined( _GAMECONSOLE ) + #define _GAMECONSOLE + #endif + #define IsPC() false + #define IsGameConsole() true +#else + #define IsPC() true + #define IsGameConsole() false +#endif + +#ifdef PLATFORM_64BITS + #define IsPlatform64Bits() true +#else + #define IsPlatform64Bits() false +#endif + +// From steam/steamtypes.h +// RTime32 +// We use this 32 bit time representing real world time. +// It offers 1 second resolution beginning on January 1, 1970 (Unix time) +typedef uint32 RTime32; + +typedef float float32; +typedef double float64; + +// for when we don't care about how many bits we use +typedef unsigned int uint; + +#ifdef _MSC_VER +#pragma once +// Ensure that everybody has the right compiler version installed. The version +// number can be obtained by looking at the compiler output when you type 'cl' +// and removing the last two digits and the periods: 16.00.40219.01 becomes 160040219 +#if _MSC_FULL_VER > 180000000 + #if _MSC_FULL_VER < 180030723 + #error You must install VS 2013 Update 3 + #endif +#elif _MSC_FULL_VER > 160000000 + #if _MSC_FULL_VER < 160040219 + #error You must install VS 2010 SP1 + #endif +#else + #if _MSC_FULL_VER < 140050727 + #error You must install VS 2005 SP1 + #endif +#endif +#endif + +// This can be used to ensure the size of pointers to members when declaring +// a pointer type for a class that has only been forward declared +#ifdef _MSC_VER +#define SINGLE_INHERITANCE __single_inheritance +#define MULTIPLE_INHERITANCE __multiple_inheritance +#else +#define SINGLE_INHERITANCE +#define MULTIPLE_INHERITANCE +#endif + +#ifdef _MSC_VER +#define NO_VTABLE __declspec( novtable ) +#else +#define NO_VTABLE +#endif + +#ifdef _MSC_VER + // This indicates that a function never returns, which helps with + // generating accurate compiler warnings + #define NORETURN __declspec( noreturn ) +#else + #define NORETURN +#endif + +// This can be used to declare an abstract (interface only) class. +// Classes marked abstract should not be instantiated. If they are, and access violation will occur. +// +// Example of use: +// +// abstract_class CFoo +// { +// ... +// } +// +// MSDN __declspec(novtable) documentation: http://msdn.microsoft.com/library/default.asp?url=/library/en-us/vclang/html/_langref_novtable.asp +// +// Note: NJS: This is not enabled for regular PC, due to not knowing the implications of exporting a class with no no vtable. +// It's probable that this shouldn't be an issue, but an experiment should be done to verify this. +// +#ifndef _X360 +#define abstract_class class +#else +#define abstract_class class NO_VTABLE +#endif + + +// MSVC CRT uses 0x7fff while gcc uses MAX_INT, leading to mismatches between platforms +// As a result, we pick the least common denominator here. This should be used anywhere +// you might typically want to use RAND_MAX +#define VALVE_RAND_MAX 0x7fff + +/* +FIXME: Enable this when we no longer fear change =) + +// need these for the limits +#include <limits.h> +#include <float.h> + +// Maximum and minimum representable values +#define INT8_MAX SCHAR_MAX +#define INT16_MAX SHRT_MAX +#define INT32_MAX LONG_MAX +#define INT64_MAX (((int64)~0) >> 1) + +#define INT8_MIN SCHAR_MIN +#define INT16_MIN SHRT_MIN +#define INT32_MIN LONG_MIN +#define INT64_MIN (((int64)1) << 63) + +#define UINT8_MAX ((uint8)~0) +#define UINT16_MAX ((uint16)~0) +#define UINT32_MAX ((uint32)~0) +#define UINT64_MAX ((uint64)~0) + +#define UINT8_MIN 0 +#define UINT16_MIN 0 +#define UINT32_MIN 0 +#define UINT64_MIN 0 + +#ifndef UINT_MIN +#define UINT_MIN UINT32_MIN +#endif + +#define FLOAT32_MAX FLT_MAX +#define FLOAT64_MAX DBL_MAX + +#define FLOAT32_MIN FLT_MIN +#define FLOAT64_MIN DBL_MIN +*/ + +// portability / compiler settings +#if defined(_WIN32) && !defined(WINDED) + +#if defined(_M_IX86) +#define __i386__ 1 +#endif + +#elif POSIX +#if defined( OSX ) && defined( CARBON_WORKAROUND ) +#define DWORD unsigned int +#else +typedef unsigned int DWORD; +#endif +typedef unsigned short WORD; +typedef void * HINSTANCE; +#define _MAX_PATH PATH_MAX +#define __cdecl +#define __stdcall +#define __declspec + +#endif // defined(_WIN32) && !defined(WINDED) + +#define MAX_FILEPATH 512 + +// Defines MAX_PATH +#ifndef MAX_PATH +#define MAX_PATH 260 +#endif + +#ifdef _WIN32 +#define MAX_UNICODE_PATH 32767 +#else +#define MAX_UNICODE_PATH MAX_PATH +#endif + +#define MAX_UNICODE_PATH_IN_UTF8 MAX_UNICODE_PATH*4 + +#if !defined( offsetof ) + #ifdef __GNUC__ + #define offsetof( type, var ) __builtin_offsetof( type, var ) + #else + #define offsetof(s,m) (size_t)&(((s *)0)->m) + #endif +#endif // !defined( offsetof ) + + +#define ALIGN_VALUE( val, alignment ) ( ( val + alignment - 1 ) & ~( alignment - 1 ) ) // need macro for constant expression + +// Used to step into the debugger +#if defined( _WIN32 ) && !defined( _X360 ) +#define DebuggerBreak() __debugbreak() +#elif defined( _X360 ) +#define DebuggerBreak() DebugBreak() +#else + // On OSX, SIGTRAP doesn't really stop the thread cold when debugging. + // So if being debugged, use INT3 which is precise. +#ifdef OSX +#define DebuggerBreak() if ( Plat_IsInDebugSession() ) { __asm ( "int $3" ); } else { raise(SIGTRAP); } +#else +#define DebuggerBreak() raise(SIGTRAP) +#endif +#endif +#define DebuggerBreakIfDebugging() if ( !Plat_IsInDebugSession() ) ; else DebuggerBreak() + +#ifdef STAGING_ONLY +#define DebuggerBreakIfDebugging_StagingOnly() if ( !Plat_IsInDebugSession() ) ; else DebuggerBreak() +#else +#define DebuggerBreakIfDebugging_StagingOnly() +#endif + +// Allows you to specify code that should only execute if we are in a staging build. Otherwise the code noops. +#ifdef STAGING_ONLY +#define STAGING_ONLY_EXEC( _exec ) do { _exec; } while (0) +#else +#define STAGING_ONLY_EXEC( _exec ) do { } while (0) +#endif + +// C functions for external declarations that call the appropriate C++ methods +#ifndef EXPORT + #ifdef _WIN32 + #define EXPORT _declspec( dllexport ) + #else + #define EXPORT /* */ + #endif +#endif + +#if defined __i386__ && !defined __linux__ + #define id386 1 +#else + #define id386 0 +#endif // __i386__ + +// decls for aligning data +#ifdef _WIN32 + #define DECL_ALIGN(x) __declspec(align(x)) + +#elif GNUC + #define DECL_ALIGN(x) __attribute__((aligned(x))) +#else + #define DECL_ALIGN(x) /* */ +#endif + +#ifdef _MSC_VER +// MSVC has the align at the start of the struct +#define ALIGN4 DECL_ALIGN(4) +#define ALIGN8 DECL_ALIGN(8) +#define ALIGN16 DECL_ALIGN(16) +#define ALIGN32 DECL_ALIGN(32) +#define ALIGN128 DECL_ALIGN(128) + +#define ALIGN4_POST +#define ALIGN8_POST +#define ALIGN16_POST +#define ALIGN32_POST +#define ALIGN128_POST +#elif defined( GNUC ) +// gnuc has the align decoration at the end +#define ALIGN4 +#define ALIGN8 +#define ALIGN16 +#define ALIGN32 +#define ALIGN128 + +#define ALIGN4_POST DECL_ALIGN(4) +#define ALIGN8_POST DECL_ALIGN(8) +#define ALIGN16_POST DECL_ALIGN(16) +#define ALIGN32_POST DECL_ALIGN(32) +#define ALIGN128_POST DECL_ALIGN(128) +#else +#error +#endif + +// Pull in the /analyze code annotations. +#include "annotations.h" + +//----------------------------------------------------------------------------- +// Convert int<-->pointer, avoiding 32/64-bit compiler warnings: +//----------------------------------------------------------------------------- +#define INT_TO_POINTER( i ) (void *)( ( i ) + (char *)NULL ) +#define POINTER_TO_INT( p ) ( (int)(uintp)( p ) ) + + +//----------------------------------------------------------------------------- +// Stack-based allocation related helpers +//----------------------------------------------------------------------------- +#if defined( GNUC ) + #define stackalloc( _size ) alloca( ALIGN_VALUE( _size, 16 ) ) +#ifdef _LINUX + #define mallocsize( _p ) ( malloc_usable_size( _p ) ) +#elif defined(OSX) + #define mallocsize( _p ) ( malloc_size( _p ) ) +#else +#error +#endif +#elif defined ( _WIN32 ) + #define stackalloc( _size ) _alloca( ALIGN_VALUE( _size, 16 ) ) + #define mallocsize( _p ) ( _msize( _p ) ) +#endif + +#define stackfree( _p ) 0 + +// Linux had a few areas where it didn't construct objects in the same order that Windows does. +// So when CVProfile::CVProfile() would access g_pMemAlloc, it would crash because the allocator wasn't initalized yet. +#ifdef POSIX + #define CONSTRUCT_EARLY __attribute__((init_priority(101))) +#else + #define CONSTRUCT_EARLY + #endif + +#if defined(_MSC_VER) + #define SELECTANY __declspec(selectany) + #define RESTRICT __restrict + #define RESTRICT_FUNC __declspec(restrict) + #define FMTFUNCTION( a, b ) +#elif defined(GNUC) + #define SELECTANY __attribute__((weak)) + #if defined(LINUX) && !defined(DEDICATED) + #define RESTRICT + #else + #define RESTRICT __restrict + #endif + #define RESTRICT_FUNC + // squirrel.h does a #define printf DevMsg which leads to warnings when we try + // to use printf as the prototype format function. Using __printf__ instead. + #define FMTFUNCTION( fmtargnumber, firstvarargnumber ) __attribute__ (( format( __printf__, fmtargnumber, firstvarargnumber ))) +#else + #define SELECTANY static + #define RESTRICT + #define RESTRICT_FUNC + #define FMTFUNCTION( a, b ) +#endif + +#if defined( _WIN32 ) + + // Used for dll exporting and importing + #define DLL_EXPORT extern "C" __declspec( dllexport ) + #define DLL_IMPORT extern "C" __declspec( dllimport ) + + // Can't use extern "C" when DLL exporting a class + #define DLL_CLASS_EXPORT __declspec( dllexport ) + #define DLL_CLASS_IMPORT __declspec( dllimport ) + + // Can't use extern "C" when DLL exporting a global + #define DLL_GLOBAL_EXPORT extern __declspec( dllexport ) + #define DLL_GLOBAL_IMPORT extern __declspec( dllimport ) + + #define DLL_LOCAL + +#elif defined GNUC +// Used for dll exporting and importing +#define DLL_EXPORT extern "C" __attribute__ ((visibility("default"))) +#define DLL_IMPORT extern "C" + +// Can't use extern "C" when DLL exporting a class +#define DLL_CLASS_EXPORT __attribute__ ((visibility("default"))) +#define DLL_CLASS_IMPORT + +// Can't use extern "C" when DLL exporting a global +#define DLL_GLOBAL_EXPORT extern __attribute ((visibility("default"))) +#define DLL_GLOBAL_IMPORT extern + +#define DLL_LOCAL __attribute__ ((visibility("hidden"))) + +#else +#error "Unsupported Platform." +#endif + +// Used for standard calling conventions +#if defined( _WIN32 ) && !defined( _X360 ) + #define STDCALL __stdcall + #define FASTCALL __fastcall + #define FORCEINLINE __forceinline + // GCC 3.4.1 has a bug in supporting forced inline of templated functions + // this macro lets us not force inlining in that case + #define FORCEINLINE_TEMPLATE __forceinline +#elif defined( _X360 ) + #define STDCALL __stdcall + #ifdef FORCEINLINE + #undef FORCEINLINE +#endif + #define FORCEINLINE __forceinline + #define FORCEINLINE_TEMPLATE __forceinline + #else + #define STDCALL + #define FASTCALL + #ifdef _LINUX_DEBUGGABLE + #define FORCEINLINE + #else + #define FORCEINLINE inline __attribute__ ((always_inline)) + #endif + // GCC 3.4.1 has a bug in supporting forced inline of templated functions + // this macro lets us not force inlining in that case + #define FORCEINLINE_TEMPLATE inline +// #define __stdcall __attribute__ ((__stdcall__)) +#endif + +// Force a function call site -not- to inlined. (useful for profiling) +#define DONT_INLINE(a) (((int)(a)+1)?(a):(a)) + +// Pass hints to the compiler to prevent it from generating unnessecary / stupid code +// in certain situations. Several compilers other than MSVC also have an equivilent +// construct. +// +// Essentially the 'Hint' is that the condition specified is assumed to be true at +// that point in the compilation. If '0' is passed, then the compiler assumes that +// any subsequent code in the same 'basic block' is unreachable, and thus usually +// removed. +#ifdef _MSC_VER + #define HINT(THE_HINT) __assume((THE_HINT)) +#else + #define HINT(THE_HINT) 0 +#endif + +// Marks the codepath from here until the next branch entry point as unreachable, +// and asserts if any attempt is made to execute it. +#define UNREACHABLE() { Assert(0); HINT(0); } + +// In cases where no default is present or appropriate, this causes MSVC to generate +// as little code as possible, and throw an assertion in debug. +#define NO_DEFAULT default: UNREACHABLE(); + + +#ifdef _WIN32 + +// Remove warnings from warning level 4. +#pragma warning(disable : 4514) // warning C4514: 'acosl' : unreferenced inline function has been removed +#pragma warning(disable : 4100) // warning C4100: 'hwnd' : unreferenced formal parameter +#pragma warning(disable : 4127) // warning C4127: conditional expression is constant +#pragma warning(disable : 4512) // warning C4512: 'InFileRIFF' : assignment operator could not be generated +#pragma warning(disable : 4611) // warning C4611: interaction between '_setjmp' and C++ object destruction is non-portable +#pragma warning(disable : 4710) // warning C4710: function 'x' not inlined +#pragma warning(disable : 4702) // warning C4702: unreachable code +#pragma warning(disable : 4505) // unreferenced local function has been removed +#pragma warning(disable : 4239) // nonstandard extension used : 'argument' ( conversion from class Vector to class Vector& ) +#pragma warning(disable : 4097) // typedef-name 'BaseClass' used as synonym for class-name 'CFlexCycler::CBaseFlex' +#pragma warning(disable : 4324) // Padding was added at the end of a structure +#pragma warning(disable : 4244) // type conversion warning. +#pragma warning(disable : 4305) // truncation from 'const double ' to 'float ' +#pragma warning(disable : 4786) // Disable warnings about long symbol names +#pragma warning(disable : 4250) // 'X' : inherits 'Y::Z' via dominance +#pragma warning(disable : 4201) // nonstandard extension used : nameless struct/union +#pragma warning(disable : 4481) // warning C4481: nonstandard extension used: override specifier 'override' +#pragma warning(disable : 4748) // warning C4748: /GS can not protect parameters and local variables from local buffer overrun because optimizations are disabled in function + +#if _MSC_VER >= 1300 +#pragma warning(disable : 4511) // Disable warnings about private copy constructors +#pragma warning(disable : 4121) // warning C4121: 'symbol' : alignment of a member was sensitive to packing +#pragma warning(disable : 4530) // warning C4530: C++ exception handler used, but unwind semantics are not enabled. Specify /EHsc (disabled due to std headers having exception syntax) +#endif + +#if _MSC_VER >= 1400 +#pragma warning(disable : 4996) // functions declared deprecated +#endif + + +#endif // _WIN32 + +#if defined( LINUX ) && ((__GNUC__ * 100) + __GNUC_MINOR__) >= 406 + // based on some Jonathan Wakely macros on the net... + #define GCC_DIAG_STR(s) #s + #define GCC_DIAG_JOINSTR(x,y) GCC_DIAG_STR(x ## y) + #define GCC_DIAG_DO_PRAGMA(x) _Pragma (#x) + #define GCC_DIAG_PRAGMA(x) GCC_DIAG_DO_PRAGMA(GCC diagnostic x) + + #define GCC_DIAG_PUSH_OFF(x) GCC_DIAG_PRAGMA(push) GCC_DIAG_PRAGMA(ignored GCC_DIAG_JOINSTR(-W,x)) + #define GCC_DIAG_POP() GCC_DIAG_PRAGMA(pop) +#else + #define GCC_DIAG_PUSH_OFF(x) + #define GCC_DIAG_POP() +#endif + +#ifdef LINUX +#pragma GCC diagnostic ignored "-Wconversion-null" // passing NULL to non-pointer argument 1 +#pragma GCC diagnostic ignored "-Wpointer-arith" // NULL used in arithmetic. Ie, vpanel == NULL where VPANEL is uint. +#pragma GCC diagnostic ignored "-Wswitch" // enumeration values not handled in switch +#endif + +#ifdef OSX +#pragma GCC diagnostic ignored "-Wconversion-null" // passing NULL to non-pointer argument 1 +#pragma GCC diagnostic ignored "-Wnull-arithmetic" // NULL used in arithmetic. Ie, vpanel == NULL where VPANEL is uint. +#pragma GCC diagnostic ignored "-Wswitch-enum" // enumeration values not handled in switch +#pragma GCC diagnostic ignored "-Wswitch" // enumeration values not handled in switch +#endif + + +// When we port to 64 bit, we'll have to resolve the int, ptr vs size_t 32/64 bit problems... +#if !defined( _WIN64 ) +#pragma warning( disable : 4267 ) // conversion from 'size_t' to 'int', possible loss of data +#pragma warning( disable : 4311 ) // pointer truncation from 'char *' to 'int' +#pragma warning( disable : 4312 ) // conversion from 'unsigned int' to 'memhandle_t' of greater size +#endif + + +#ifdef POSIX +#define _stricmp stricmp +#define strcmpi stricmp +#define stricmp strcasecmp +#define _vsnprintf vsnprintf +#define _alloca alloca +#ifdef _snprintf +#undef _snprintf +#endif +#define _snprintf snprintf +#define GetProcAddress dlsym +#define _chdir chdir +#define _strnicmp strnicmp +#define strnicmp strncasecmp +#define _getcwd getcwd +#define _snwprintf swprintf +#define swprintf_s swprintf +#define wcsicmp _wcsicmp +#define _wcsicmp wcscmp +#define _finite finite +#define _tempnam tempnam +#define _unlink unlink +#define _access access +#define _mkdir(dir) mkdir( dir, S_IRWXU | S_IRWXG | S_IRWXO ) +#define _wtoi(arg) wcstol(arg, NULL, 10) +#define _wtoi64(arg) wcstoll(arg, NULL, 10) + +typedef uint32 HMODULE; +typedef void *HANDLE; +#endif + +//----------------------------------------------------------------------------- +// fsel +//----------------------------------------------------------------------------- +#ifndef _X360 + +static FORCEINLINE float fsel(float fComparand, float fValGE, float fLT) +{ + return fComparand >= 0 ? fValGE : fLT; +} +static FORCEINLINE double fsel(double fComparand, double fValGE, double fLT) +{ + return fComparand >= 0 ? fValGE : fLT; +} + +#else + +// __fsel(double fComparand, double fValGE, double fLT) == fComparand >= 0 ? fValGE : fLT +// this is much faster than if ( aFloat > 0 ) { x = .. } +#define fsel __fsel + +#endif + + +//----------------------------------------------------------------------------- +// FP exception handling +//----------------------------------------------------------------------------- +//#define CHECK_FLOAT_EXCEPTIONS 1 + +#if !defined( _X360 ) +#if defined( _MSC_VER ) + + #if defined( PLATFORM_WINDOWS_PC64 ) + inline void SetupFPUControlWord() + { + } + #else + inline void SetupFPUControlWordForceExceptions() + { + // use local to get and store control word + uint16 tmpCtrlW; + __asm + { + fnclex /* clear all current exceptions */ + fnstcw word ptr [tmpCtrlW] /* get current control word */ + and [tmpCtrlW], 0FCC0h /* Keep infinity control + rounding control */ + or [tmpCtrlW], 0230h /* set to 53-bit, mask only inexact, underflow */ + fldcw word ptr [tmpCtrlW] /* put new control word in FPU */ + } + } + + #ifdef CHECK_FLOAT_EXCEPTIONS + + inline void SetupFPUControlWord() + { + SetupFPUControlWordForceExceptions(); + } + + #else + + inline void SetupFPUControlWord() + { + // use local to get and store control word + uint16 tmpCtrlW; + __asm + { + fnstcw word ptr [tmpCtrlW] /* get current control word */ + and [tmpCtrlW], 0FCC0h /* Keep infinity control + rounding control */ + or [tmpCtrlW], 023Fh /* set to 53-bit, mask only inexact, underflow */ + fldcw word ptr [tmpCtrlW] /* put new control word in FPU */ + } + } + + #endif + #endif + +#else + + inline void SetupFPUControlWord() + { + __volatile unsigned short int __cw; + __asm __volatile ("fnstcw %0" : "=m" (__cw)); + __cw = __cw & 0x0FCC0; // keep infinity control, keep rounding mode + __cw = __cw | 0x023F; // set 53-bit, no exceptions + __asm __volatile ("fldcw %0" : : "m" (__cw)); + } + +#endif // _MSC_VER + +#else + + #ifdef _DEBUG + FORCEINLINE bool IsFPUControlWordSet() + { + float f = 0.996f; + union + { + double flResult; + int pResult[2]; + }; + flResult = __fctiw( f ); + return ( pResult[1] == 1 ); + } + #endif + + inline void SetupFPUControlWord() + { + // Set round-to-nearest in FPSCR + // (cannot assemble, must use op-code form) + __emit( 0xFF80010C ); // mtfsfi 7,0 + + // Favour compatibility over speed (make sure the VPU set to Java-compliant mode) + // NOTE: the VPU *always* uses round-to-nearest + __vector4 a = { 0.0f, 0.0f, 0.0f, 0.0f }; + a; // Avoid compiler warning + __asm + { + mtvscr a; // Clear the Vector Status & Control Register to zero + } + } + +#endif // _X360 + +//----------------------------------------------------------------------------- +// Purpose: Standard functions for handling endian-ness +//----------------------------------------------------------------------------- + +//------------------------------------- +// Basic swaps +//------------------------------------- + +template <typename T> +inline T WordSwapC( T w ) +{ + uint16 temp; + + temp = ((*((uint16 *)&w) & 0xff00) >> 8); + temp |= ((*((uint16 *)&w) & 0x00ff) << 8); + + return *((T*)&temp); +} + +template <typename T> +inline T DWordSwapC( T dw ) +{ + uint32 temp; + + temp = *((uint32 *)&dw) >> 24; + temp |= ((*((uint32 *)&dw) & 0x00FF0000) >> 8); + temp |= ((*((uint32 *)&dw) & 0x0000FF00) << 8); + temp |= ((*((uint32 *)&dw) & 0x000000FF) << 24); + + return *((T*)&temp); +} + +template <typename T> +inline T QWordSwapC( T dw ) +{ + // Assert sizes passed to this are already correct, otherwise + // the cast to uint64 * below is unsafe and may have wrong results + // or even crash. + PLAT_COMPILE_TIME_ASSERT( sizeof( dw ) == sizeof(uint64) ); + + uint64 temp; + + temp = *((uint64 *)&dw) >> 56; + temp |= ((*((uint64 *)&dw) & 0x00FF000000000000ull) >> 40); + temp |= ((*((uint64 *)&dw) & 0x0000FF0000000000ull) >> 24); + temp |= ((*((uint64 *)&dw) & 0x000000FF00000000ull) >> 8); + temp |= ((*((uint64 *)&dw) & 0x00000000FF000000ull) << 8); + temp |= ((*((uint64 *)&dw) & 0x0000000000FF0000ull) << 24); + temp |= ((*((uint64 *)&dw) & 0x000000000000FF00ull) << 40); + temp |= ((*((uint64 *)&dw) & 0x00000000000000FFull) << 56); + + return *((T*)&temp); +} + +//------------------------------------- +// Fast swaps +//------------------------------------- + +#if defined( _X360 ) + + #define WordSwap WordSwap360Intr + #define DWordSwap DWordSwap360Intr + + template <typename T> + inline T WordSwap360Intr( T w ) + { + T output; + __storeshortbytereverse( w, 0, &output ); + return output; + } + + template <typename T> + inline T DWordSwap360Intr( T dw ) + { + T output; + __storewordbytereverse( dw, 0, &output ); + return output; + } + +#elif defined( _MSC_VER ) && !defined( PLATFORM_WINDOWS_PC64 ) + + #define WordSwap WordSwapAsm + #define DWordSwap DWordSwapAsm + + #pragma warning(push) + #pragma warning (disable:4035) // no return value + + template <typename T> + inline T WordSwapAsm( T w ) + { + __asm + { + mov ax, w + xchg al, ah + } + } + + template <typename T> + inline T DWordSwapAsm( T dw ) + { + __asm + { + mov eax, dw + bswap eax + } + } + + #pragma warning(pop) + +#else + + #define WordSwap WordSwapC + #define DWordSwap DWordSwapC + +#endif + +// No ASM implementation for this yet +#define QWordSwap QWordSwapC + +//------------------------------------- +// The typically used methods. +//------------------------------------- + +#if defined(__i386__) && !defined(VALVE_LITTLE_ENDIAN) +#define VALVE_LITTLE_ENDIAN 1 +#endif + +#if defined( _SGI_SOURCE ) || defined( _X360 ) +#define VALVE_BIG_ENDIAN 1 +#endif + +// If a swapped float passes through the fpu, the bytes may get changed. +// Prevent this by swapping floats as DWORDs. +#define SafeSwapFloat( pOut, pIn ) (*((uint*)pOut) = DWordSwap( *((uint*)pIn) )) + +#if defined(VALVE_LITTLE_ENDIAN) + +#define BigShort( val ) WordSwap( val ) +#define BigWord( val ) WordSwap( val ) +#define BigLong( val ) DWordSwap( val ) +#define BigDWord( val ) DWordSwap( val ) +#define LittleShort( val ) ( val ) +#define LittleWord( val ) ( val ) +#define LittleLong( val ) ( val ) +#define LittleDWord( val ) ( val ) +#define LittleQWord( val ) ( val ) +#define SwapShort( val ) BigShort( val ) +#define SwapWord( val ) BigWord( val ) +#define SwapLong( val ) BigLong( val ) +#define SwapDWord( val ) BigDWord( val ) + +// Pass floats by pointer for swapping to avoid truncation in the fpu +#define BigFloat( pOut, pIn ) SafeSwapFloat( pOut, pIn ) +#define LittleFloat( pOut, pIn ) ( *pOut = *pIn ) +#define SwapFloat( pOut, pIn ) BigFloat( pOut, pIn ) + +#elif defined(VALVE_BIG_ENDIAN) + +#define BigShort( val ) ( val ) +#define BigWord( val ) ( val ) +#define BigLong( val ) ( val ) +#define BigDWord( val ) ( val ) +#define LittleShort( val ) WordSwap( val ) +#define LittleWord( val ) WordSwap( val ) +#define LittleLong( val ) DWordSwap( val ) +#define LittleDWord( val ) DWordSwap( val ) +#define LittleQWord( val ) QWordSwap( val ) +#define SwapShort( val ) LittleShort( val ) +#define SwapWord( val ) LittleWord( val ) +#define SwapLong( val ) LittleLong( val ) +#define SwapDWord( val ) LittleDWord( val ) + +// Pass floats by pointer for swapping to avoid truncation in the fpu +#define BigFloat( pOut, pIn ) ( *pOut = *pIn ) +#define LittleFloat( pOut, pIn ) SafeSwapFloat( pOut, pIn ) +#define SwapFloat( pOut, pIn ) LittleFloat( pOut, pIn ) + +#else + +// @Note (toml 05-02-02): this technique expects the compiler to +// optimize the expression and eliminate the other path. On any new +// platform/compiler this should be tested. +inline short BigShort( short val ) { int test = 1; return ( *(char *)&test == 1 ) ? WordSwap( val ) : val; } +inline uint16 BigWord( uint16 val ) { int test = 1; return ( *(char *)&test == 1 ) ? WordSwap( val ) : val; } +inline long BigLong( long val ) { int test = 1; return ( *(char *)&test == 1 ) ? DWordSwap( val ) : val; } +inline uint32 BigDWord( uint32 val ) { int test = 1; return ( *(char *)&test == 1 ) ? DWordSwap( val ) : val; } +inline short LittleShort( short val ) { int test = 1; return ( *(char *)&test == 1 ) ? val : WordSwap( val ); } +inline uint16 LittleWord( uint16 val ) { int test = 1; return ( *(char *)&test == 1 ) ? val : WordSwap( val ); } +inline long LittleLong( long val ) { int test = 1; return ( *(char *)&test == 1 ) ? val : DWordSwap( val ); } +inline uint32 LittleDWord( uint32 val ) { int test = 1; return ( *(char *)&test == 1 ) ? val : DWordSwap( val ); } +inline uint64 LittleQWord( uint64 val ) { int test = 1; return ( *(char *)&test == 1 ) ? val : QWordSwap( val ); } +inline short SwapShort( short val ) { return WordSwap( val ); } +inline uint16 SwapWord( uint16 val ) { return WordSwap( val ); } +inline long SwapLong( long val ) { return DWordSwap( val ); } +inline uint32 SwapDWord( uint32 val ) { return DWordSwap( val ); } + +// Pass floats by pointer for swapping to avoid truncation in the fpu +inline void BigFloat( float *pOut, const float *pIn ) { int test = 1; ( *(char *)&test == 1 ) ? SafeSwapFloat( pOut, pIn ) : ( *pOut = *pIn ); } +inline void LittleFloat( float *pOut, const float *pIn ) { int test = 1; ( *(char *)&test == 1 ) ? ( *pOut = *pIn ) : SafeSwapFloat( pOut, pIn ); } +inline void SwapFloat( float *pOut, const float *pIn ) { SafeSwapFloat( pOut, pIn ); } + +#endif + +#if _X360 +FORCEINLINE unsigned long LoadLittleDWord( const unsigned long *base, unsigned int dwordIndex ) + { + return __loadwordbytereverse( dwordIndex<<2, base ); + } + +FORCEINLINE void StoreLittleDWord( unsigned long *base, unsigned int dwordIndex, unsigned long dword ) + { + __storewordbytereverse( dword, dwordIndex<<2, base ); + } +#else +FORCEINLINE unsigned long LoadLittleDWord( const unsigned long *base, unsigned int dwordIndex ) + { + return LittleDWord( base[dwordIndex] ); + } + +FORCEINLINE void StoreLittleDWord( unsigned long *base, unsigned int dwordIndex, unsigned long dword ) + { + base[dwordIndex] = LittleDWord(dword); + } +#endif + + +//----------------------------------------------------------------------------- +// DLL export for platform utilities +//----------------------------------------------------------------------------- +#ifndef STATIC_TIER0 + +#ifdef TIER0_DLL_EXPORT +#define PLATFORM_INTERFACE DLL_EXPORT +#define PLATFORM_OVERLOAD DLL_GLOBAL_EXPORT +#define PLATFORM_CLASS DLL_CLASS_EXPORT +#else +#define PLATFORM_INTERFACE DLL_IMPORT +#define PLATFORM_OVERLOAD DLL_GLOBAL_IMPORT +#define PLATFORM_CLASS DLL_CLASS_IMPORT +#endif + +#else // BUILD_AS_DLL + +#define PLATFORM_INTERFACE extern +#define PLATFORM_OVERLOAD +#define PLATFORM_CLASS + +#endif // BUILD_AS_DLL + + +// When in benchmark mode, the timer returns a simple incremented value each time you call it. +// +// It should not be changed after startup unless you really know what you're doing. The only place +// that should do this is the benchmark code itself so it can output a legit duration. +PLATFORM_INTERFACE void Plat_SetBenchmarkMode( bool bBenchmarkMode ); +PLATFORM_INTERFACE bool Plat_IsInBenchmarkMode(); + + +PLATFORM_INTERFACE double Plat_FloatTime(); // Returns time in seconds since the module was loaded. +PLATFORM_INTERFACE uint32 Plat_MSTime(); // Time in milliseconds. +PLATFORM_INTERFACE uint64 Plat_USTime(); // Time in microseconds. +PLATFORM_INTERFACE char * Plat_ctime( const time_t *timep, char *buf, size_t bufsize ); +PLATFORM_INTERFACE void Plat_GetModuleFilename( char *pOut, int nMaxBytes ); + +PLATFORM_INTERFACE void Plat_ExitProcess( int nCode ); + +//called to exit the process due to a fatal error. This allows for the application to handle providing a hook as well which can be called +//before exiting +PLATFORM_INTERFACE void Plat_ExitProcessWithError( int nCode, bool bGenerateMinidump = false ); + +//sets the callback that will be triggered by Plat_ExitProcessWithError. NULL is valid. The return value true indicates that +//the exit has been handled and no further processing should be performed. False will cause a minidump to be generated, and the process +//to be terminated +typedef bool (*ExitProcessWithErrorCBFn)( int nCode ); +PLATFORM_INTERFACE void Plat_SetExitProcessWithErrorCB( ExitProcessWithErrorCBFn pfnCB ); + +PLATFORM_INTERFACE struct tm * Plat_gmtime( const time_t *timep, struct tm *result ); +PLATFORM_INTERFACE time_t Plat_timegm( struct tm *timeptr ); +PLATFORM_INTERFACE struct tm * Plat_localtime( const time_t *timep, struct tm *result ); + +#if defined( _WIN32 ) && defined( _MSC_VER ) && ( _MSC_VER >= 1400 ) + extern "C" unsigned __int64 __rdtsc(); + #pragma intrinsic(__rdtsc) +#endif + +inline uint64 Plat_Rdtsc() +{ +#if defined( _X360 ) + return ( uint64 )__mftb32(); +#elif defined( _WIN64 ) + return ( uint64 )__rdtsc(); +#elif defined( _WIN32 ) + #if defined( _MSC_VER ) && ( _MSC_VER >= 1400 ) + return ( uint64 )__rdtsc(); + #else + __asm rdtsc; + __asm ret; + #endif +#elif defined( __i386__ ) + uint64 val; + __asm__ __volatile__ ( "rdtsc" : "=A" (val) ); + return val; +#elif defined( __x86_64__ ) + uint32 lo, hi; + __asm__ __volatile__ ( "rdtsc" : "=a" (lo), "=d" (hi)); + return ( ( ( uint64 )hi ) << 32 ) | lo; +#else + #error +#endif +} + +// b/w compatibility +#define Sys_FloatTime Plat_FloatTime + +// Protect against bad auto operator= +#define DISALLOW_OPERATOR_EQUAL( _classname ) \ + private: \ + _classname &operator=( const _classname & ); \ + public: + +// Define a reasonable operator= +#define IMPLEMENT_OPERATOR_EQUAL( _classname ) \ + public: \ + _classname &operator=( const _classname &src ) \ + { \ + memcpy( this, &src, sizeof(_classname) ); \ + return *this; \ + } + +// Processor Information: +struct CPUInformation +{ + int m_Size; // Size of this structure, for forward compatability. + + bool m_bRDTSC : 1, // Is RDTSC supported? + m_bCMOV : 1, // Is CMOV supported? + m_bFCMOV : 1, // Is FCMOV supported? + m_bSSE : 1, // Is SSE supported? + m_bSSE2 : 1, // Is SSE2 Supported? + m_b3DNow : 1, // Is 3DNow! Supported? + m_bMMX : 1, // Is MMX supported? + m_bHT : 1; // Is HyperThreading supported? + + uint8 m_nLogicalProcessors; // Number op logical processors. + uint8 m_nPhysicalProcessors; // Number of physical processors + + bool m_bSSE3 : 1, + m_bSSSE3 : 1, + m_bSSE4a : 1, + m_bSSE41 : 1, + m_bSSE42 : 1; + + int64 m_Speed; // In cycles per second. + + tchar* m_szProcessorID; // Processor vendor Identification. + + uint32 m_nModel; + uint32 m_nFeatures[3]; + + CPUInformation(): m_Size(0){} +}; + +// Have to return a pointer, not a reference, because references are not compatible with the +// extern "C" implied by PLATFORM_INTERFACE. +PLATFORM_INTERFACE const CPUInformation* GetCPUInformation(); + +#define MEMORY_INFORMATION_VERSION 0 + +struct MemoryInformation +{ + int m_nStructVersion; + + uint m_nPhysicalRamMbTotal; + uint m_nPhysicalRamMbAvailable; + + uint m_nVirtualRamMbTotal; + uint m_nVirtualRamMbAvailable; + + inline MemoryInformation() + { + memset( this, 0, sizeof( *this ) ); + m_nStructVersion = MEMORY_INFORMATION_VERSION; + } +}; + +// Returns true if the passed in MemoryInformation structure was filled out, otherwise false. +PLATFORM_INTERFACE bool GetMemoryInformation( MemoryInformation *pOutMemoryInfo ); + +PLATFORM_INTERFACE float GetCPUUsage(); + +PLATFORM_INTERFACE void GetCurrentDate( int *pDay, int *pMonth, int *pYear ); + +// ---------------------------------------------------------------------------------- // +// Performance Monitoring Events - L2 stats etc... +// ---------------------------------------------------------------------------------- // +PLATFORM_INTERFACE void InitPME(); +PLATFORM_INTERFACE void ShutdownPME(); + +//----------------------------------------------------------------------------- +// Thread related functions +//----------------------------------------------------------------------------- + +// Sets a hardware data breakpoint on the given address. Currently Win32-only. +// Specify 1, 2, or 4 bytes for nWatchBytes; pass 0 to unregister the address. +PLATFORM_INTERFACE void Plat_SetHardwareDataBreakpoint( const void *pAddress, int nWatchBytes, bool bBreakOnRead ); + +// Apply current hardware data breakpoints to a newly created thread. +PLATFORM_INTERFACE void Plat_ApplyHardwareDataBreakpointsToNewThread( unsigned long dwThreadID ); + +//----------------------------------------------------------------------------- +// Process related functions +//----------------------------------------------------------------------------- +PLATFORM_INTERFACE const tchar *Plat_GetCommandLine(); +#ifndef _WIN32 +// helper function for OS's that don't have a ::GetCommandLine() call +PLATFORM_INTERFACE void Plat_SetCommandLine( const char *cmdLine ); +#endif +PLATFORM_INTERFACE const char *Plat_GetCommandLineA(); + +//----------------------------------------------------------------------------- +// Security related functions +//----------------------------------------------------------------------------- +// Ensure that the hardware key's drivers have been installed. +PLATFORM_INTERFACE bool Plat_VerifyHardwareKeyDriver(); + +// Ok, so this isn't a very secure way to verify the hardware key for now. It +// is primarially depending on the fact that all the binaries have been wrapped +// with the secure wrapper provided by the hardware keys vendor. +PLATFORM_INTERFACE bool Plat_VerifyHardwareKey(); + +// The same as above, but notifies user with a message box when the key isn't in +// and gives him an opportunity to correct the situation. +PLATFORM_INTERFACE bool Plat_VerifyHardwareKeyPrompt(); + +// Can be called in real time, doesn't perform the verify every frame. Mainly just +// here to allow the game to drop out quickly when the key is removed, rather than +// allowing the wrapper to pop up it's own blocking dialog, which the engine doesn't +// like much. +PLATFORM_INTERFACE bool Plat_FastVerifyHardwareKey(); + +//----------------------------------------------------------------------------- +// Just logs file and line to simple.log +//----------------------------------------------------------------------------- +PLATFORM_INTERFACE void* Plat_SimpleLog( const tchar* file, int line ); + +#if _X360 +#define Plat_FastMemset XMemSet +#define Plat_FastMemcpy XMemCpy +#else +#define Plat_FastMemset memset +#define Plat_FastMemcpy memcpy +#endif + +//----------------------------------------------------------------------------- +// Returns true if debugger attached, false otherwise +//----------------------------------------------------------------------------- +#if defined(_WIN32) || defined(LINUX) || defined(OSX) +PLATFORM_INTERFACE bool Plat_IsInDebugSession(); +PLATFORM_INTERFACE void Plat_DebugString( const char * ); +#else +inline bool Plat_IsInDebugSession( bool bForceRecheck = false ) { return false; } +#define Plat_DebugString(s) ((void)0) +#endif + +//----------------------------------------------------------------------------- +// Returns true if running on a 64 bit (windows) OS +//----------------------------------------------------------------------------- +PLATFORM_INTERFACE bool Is64BitOS(); + + +//----------------------------------------------------------------------------- +// XBOX Components valid in PC compilation space +//----------------------------------------------------------------------------- + +#define XBOX_DVD_SECTORSIZE 2048 +#define XBOX_DVD_ECC_SIZE 32768 // driver reads in quantum ECC blocks +#define XBOX_HDD_SECTORSIZE 512 + +// Custom windows messages for Xbox input +#define WM_XREMOTECOMMAND (WM_USER + 100) +#define WM_XCONTROLLER_KEY (WM_USER + 101) +#define WM_SYS_UI (WM_USER + 102) +#define WM_SYS_SIGNINCHANGED (WM_USER + 103) +#define WM_SYS_STORAGEDEVICESCHANGED (WM_USER + 104) +#define WM_SYS_PROFILESETTINGCHANGED (WM_USER + 105) +#define WM_SYS_MUTELISTCHANGED (WM_USER + 106) +#define WM_SYS_INPUTDEVICESCHANGED (WM_USER + 107) +#define WM_SYS_INPUTDEVICECONFIGCHANGED (WM_USER + 108) +#define WM_LIVE_CONNECTIONCHANGED (WM_USER + 109) +#define WM_LIVE_INVITE_ACCEPTED (WM_USER + 110) +#define WM_LIVE_LINK_STATE_CHANGED (WM_USER + 111) +#define WM_LIVE_CONTENT_INSTALLED (WM_USER + 112) +#define WM_LIVE_MEMBERSHIP_PURCHASED (WM_USER + 113) +#define WM_LIVE_VOICECHAT_AWAY (WM_USER + 114) +#define WM_LIVE_PRESENCE_CHANGED (WM_USER + 115) +#define WM_FRIENDS_PRESENCE_CHANGED (WM_USER + 116) +#define WM_FRIENDS_FRIEND_ADDED (WM_USER + 117) +#define WM_FRIENDS_FRIEND_REMOVED (WM_USER + 118) +#define WM_CUSTOM_GAMEBANNERPRESSED (WM_USER + 119) +#define WM_CUSTOM_ACTIONPRESSED (WM_USER + 120) +#define WM_XMP_STATECHANGED (WM_USER + 121) +#define WM_XMP_PLAYBACKBEHAVIORCHANGED (WM_USER + 122) +#define WM_XMP_PLAYBACKCONTROLLERCHANGED (WM_USER + 123) + +inline const char *GetPlatformExt( void ) +{ + return IsX360() ? ".360" : ""; +} + +// flat view, 6 hw threads +#define XBOX_PROCESSOR_0 ( 1<<0 ) +#define XBOX_PROCESSOR_1 ( 1<<1 ) +#define XBOX_PROCESSOR_2 ( 1<<2 ) +#define XBOX_PROCESSOR_3 ( 1<<3 ) +#define XBOX_PROCESSOR_4 ( 1<<4 ) +#define XBOX_PROCESSOR_5 ( 1<<5 ) + +// core view, 3 cores with 2 hw threads each +#define XBOX_CORE_0_HWTHREAD_0 XBOX_PROCESSOR_0 +#define XBOX_CORE_0_HWTHREAD_1 XBOX_PROCESSOR_1 +#define XBOX_CORE_1_HWTHREAD_0 XBOX_PROCESSOR_2 +#define XBOX_CORE_1_HWTHREAD_1 XBOX_PROCESSOR_3 +#define XBOX_CORE_2_HWTHREAD_0 XBOX_PROCESSOR_4 +#define XBOX_CORE_2_HWTHREAD_1 XBOX_PROCESSOR_5 + +//----------------------------------------------------------------------------- +// Include additional dependant header components. +//----------------------------------------------------------------------------- +#include "tier0/fasttimer.h" + +#if defined( _X360 ) +#include "xbox/xbox_core.h" +#endif + +//----------------------------------------------------------------------------- +// Methods to invoke the constructor, copy constructor, and destructor +//----------------------------------------------------------------------------- + +template <class T> +inline T* Construct( T* pMemory ) +{ + return reinterpret_cast<T*>(::new( pMemory ) T); +} + +template <class T, typename ARG1> +inline T* Construct( T* pMemory, ARG1 a1 ) +{ + return reinterpret_cast<T*>(::new( pMemory ) T( a1 )); +} + +template <class T, typename ARG1, typename ARG2> +inline T* Construct( T* pMemory, ARG1 a1, ARG2 a2 ) +{ + return reinterpret_cast<T*>(::new( pMemory ) T( a1, a2 )); +} + +template <class T, typename ARG1, typename ARG2, typename ARG3> +inline T* Construct( T* pMemory, ARG1 a1, ARG2 a2, ARG3 a3 ) +{ + return reinterpret_cast<T*>(::new( pMemory ) T( a1, a2, a3 )); +} + +template <class T, typename ARG1, typename ARG2, typename ARG3, typename ARG4> +inline T* Construct( T* pMemory, ARG1 a1, ARG2 a2, ARG3 a3, ARG4 a4 ) +{ + return reinterpret_cast<T*>(::new( pMemory ) T( a1, a2, a3, a4 )); +} + +template <class T, typename ARG1, typename ARG2, typename ARG3, typename ARG4, typename ARG5> +inline T* Construct( T* pMemory, ARG1 a1, ARG2 a2, ARG3 a3, ARG4 a4, ARG5 a5 ) +{ + return reinterpret_cast<T*>(::new( pMemory ) T( a1, a2, a3, a4, a5 )); +} + +template <class T, class P> +inline void ConstructOneArg( T* pMemory, P const& arg) +{ + ::new( pMemory ) T(arg); +} + +template <class T, class P1, class P2 > +inline void ConstructTwoArg( T* pMemory, P1 const& arg1, P2 const& arg2) +{ + ::new( pMemory ) T(arg1, arg2); +} + +template <class T, class P1, class P2, class P3 > +inline void ConstructThreeArg( T* pMemory, P1 const& arg1, P2 const& arg2, P3 const& arg3) +{ + ::new( pMemory ) T(arg1, arg2, arg3); +} + +template <class T> +inline T* CopyConstruct( T* pMemory, T const& src ) +{ + return reinterpret_cast<T*>(::new( pMemory ) T(src)); +} + +template <class T> +inline void Destruct( T* pMemory ) +{ + pMemory->~T(); + +#ifdef _DEBUG + memset( reinterpret_cast<void*>( pMemory ), 0xDD, sizeof(T) ); +#endif +} + +// The above will error when binding to a type of: foo(*)[] -- there is no provision in c++ for knowing how many objects +// to destruct without preserving the count and calling the necessary destructors. +template <class T, size_t N> +inline void Destruct( T (*pMemory)[N] ) +{ + for ( size_t i = 0; i < N; i++ ) + { + (pMemory[i])->~T(); + } + +#ifdef _DEBUG + memset( reinterpret_cast<void*>( pMemory ), 0xDD, sizeof(*pMemory) ); +#endif +} + + +// +// GET_OUTER() +// +// A platform-independent way for a contained class to get a pointer to its +// owner. If you know a class is exclusively used in the context of some +// "outer" class, this is a much more space efficient way to get at the outer +// class than having the inner class store a pointer to it. +// +// class COuter +// { +// class CInner // Note: this does not need to be a nested class to work +// { +// void PrintAddressOfOuter() +// { +// printf( "Outer is at 0x%x\n", GET_OUTER( COuter, m_Inner ) ); +// } +// }; +// +// CInner m_Inner; +// friend class CInner; +// }; + +#define GET_OUTER( OuterType, OuterMember ) \ + ( ( OuterType * ) ( (uint8 *)this - offsetof( OuterType, OuterMember ) ) ) + + +/* TEMPLATE_FUNCTION_TABLE() + + (Note added to platform.h so platforms that correctly support templated + functions can handle portions as templated functions rather than wrapped + functions) + + Helps automate the process of creating an array of function + templates that are all specialized by a single integer. + This sort of thing is often useful in optimization work. + + For example, using TEMPLATE_FUNCTION_TABLE, this: + + TEMPLATE_FUNCTION_TABLE(int, Function, ( int blah, int blah ), 10) + { + return argument * argument; + } + + is equivilent to the following: + + (NOTE: the function has to be wrapped in a class due to code + generation bugs involved with directly specializing a function + based on a constant.) + + template<int argument> + class FunctionWrapper + { + public: + int Function( int blah, int blah ) + { + return argument*argument; + } + } + + typedef int (*FunctionType)( int blah, int blah ); + + class FunctionName + { + public: + enum { count = 10 }; + FunctionType functions[10]; + }; + + FunctionType FunctionName::functions[] = + { + FunctionWrapper<0>::Function, + FunctionWrapper<1>::Function, + FunctionWrapper<2>::Function, + FunctionWrapper<3>::Function, + FunctionWrapper<4>::Function, + FunctionWrapper<5>::Function, + FunctionWrapper<6>::Function, + FunctionWrapper<7>::Function, + FunctionWrapper<8>::Function, + FunctionWrapper<9>::Function + }; +*/ + +PLATFORM_INTERFACE bool vtune( bool resume ); + + +#define TEMPLATE_FUNCTION_TABLE(RETURN_TYPE, NAME, ARGS, COUNT) \ + \ +typedef RETURN_TYPE (FASTCALL *__Type_##NAME) ARGS; \ + \ +template<const int nArgument> \ +struct __Function_##NAME \ +{ \ + static RETURN_TYPE FASTCALL Run ARGS; \ +}; \ + \ +template <const int i> \ +struct __MetaLooper_##NAME : __MetaLooper_##NAME<i-1> \ +{ \ + __Type_##NAME func; \ + inline __MetaLooper_##NAME() { func = __Function_##NAME<i>::Run; } \ +}; \ + \ +template<> \ +struct __MetaLooper_##NAME<0> \ +{ \ + __Type_##NAME func; \ + inline __MetaLooper_##NAME() { func = __Function_##NAME<0>::Run; } \ +}; \ + \ +class NAME \ +{ \ +private: \ + static const __MetaLooper_##NAME<COUNT> m; \ +public: \ + enum { count = COUNT }; \ + static const __Type_##NAME* functions; \ +}; \ +const __MetaLooper_##NAME<COUNT> NAME::m; \ +const __Type_##NAME* NAME::functions = (__Type_##NAME*)&m; \ +template<const int nArgument> \ +RETURN_TYPE FASTCALL __Function_##NAME<nArgument>::Run ARGS + + +#define LOOP_INTERCHANGE(BOOLEAN, CODE)\ + if( (BOOLEAN) )\ + {\ + CODE;\ + } else\ + {\ + CODE;\ + } + +//----------------------------------------------------------------------------- +// Dynamic libs support +//----------------------------------------------------------------------------- +#if 0 // defined( PLATFORM_WINDOWS_PC ) + +PLATFORM_INTERFACE void *Plat_GetProcAddress( const char *pszModule, const char *pszName ); + +template <typename FUNCPTR_TYPE> +class CDynamicFunction +{ +public: + CDynamicFunction( const char *pszModule, const char *pszName, FUNCPTR_TYPE pfnFallback = NULL ) + { + m_pfn = pfnFallback; + void *pAddr = Plat_GetProcAddress( pszModule, pszName ); + if ( pAddr ) + { + m_pfn = (FUNCPTR_TYPE)pAddr; + } + } + + operator bool() { return m_pfn != NULL; } + bool operator !() { return !m_pfn; } + operator FUNCPTR_TYPE() { return m_pfn; } + +private: + FUNCPTR_TYPE m_pfn; +}; +#endif + + +// Watchdog timer support. Call Plat_BeginWatchdogTimer( nn ) to kick the timer off. if you don't call +// Plat_EndWatchdogTimer within nn seconds, the program will kick off an exception. This is for making +// sure that hung dedicated servers abort (and restart) instead of staying hung. Calling +// Plat_EndWatchdogTimer more than once or when there is no active watchdog is fine. Only does anything +// under linux right now. It should be possible to implement this functionality in windows via a +// thread, if desired. +PLATFORM_INTERFACE void Plat_BeginWatchdogTimer( int nSecs ); +PLATFORM_INTERFACE void Plat_EndWatchdogTimer( void ); +PLATFORM_INTERFACE int Plat_GetWatchdogTime( void ); + +typedef void (*Plat_WatchDogHandlerFunction_t)(void); +PLATFORM_INTERFACE void Plat_SetWatchdogHandlerFunction( Plat_WatchDogHandlerFunction_t function ); + + +//----------------------------------------------------------------------------- + +#include "tier0/valve_on.h" + +#if defined(TIER0_DLL_EXPORT) +extern "C" int V_tier0_stricmp(const char *s1, const char *s2 ); +#undef stricmp +#undef strcmpi +#define stricmp(s1,s2) V_tier0_stricmp( s1, s2 ) +#define strcmpi(s1,s2) V_tier0_stricmp( s1, s2 ) +#endif + + +#endif /* PLATFORM_H */ diff --git a/public/tier0/pmc360.h b/public/tier0/pmc360.h new file mode 100644 index 0000000..ebae346 --- /dev/null +++ b/public/tier0/pmc360.h @@ -0,0 +1,73 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: Analogous to l2cache.h, this class represents information gleaned +// from the 360's Performance Monitor Counters. In particular we +// are interested in l2 cache misses and load-hit-stores. +// +//=============================================================================// +#ifndef CPMCDATA_H +#define CPMCDATA_H +#ifdef _WIN32 +#pragma once +#endif + +#ifndef _X360 +#error This file must only be compiled for XBOX360! +#endif + + +// Warning: +// As written, this class only supports profiling thread 0, processor 0. + +class CPMCData +{ +public: + + CPMCData(); + ~CPMCData() {}; + + void Start( void ); + void End( void ); + + /// This function should be called exactly once during the lifespan of the program; + /// it will set up the counters to record the information we are interested in. + /// This will stomp on whoever else might have set the performance counters elsewhere + /// in the game. + static void InitializeOnceProgramWide( void ); + static bool IsInitialized(); + + //------------------------------------------------------------------------- + // GetL2CacheMisses + //------------------------------------------------------------------------- + uint64 GetL2CacheMisses( void ) const + { + return m_Delta.L2CacheMiss; + } + + uint64 GetLHS( void ) const + { + return m_Delta.LHS; + } + +/* +#ifdef DBGFLAG_VALIDATE + void Validate( CValidator &validator, tchar *pchName ); // Validate our internal structures +#endif // DBGFLAG_VALIDATE +*/ + +private: + /// represents saved numbers from the counters we are interested in + struct PMCounters + { + uint64 L2CacheMiss; + uint64 LHS; ///< load hit store + + PMCounters(int64 _l2cm, int64 _lhs ) : L2CacheMiss(_l2cm), LHS(_lhs) {}; + PMCounters() : L2CacheMiss(0), LHS(0) {}; + }; + + PMCounters m_OnStart; ///< values when we began the timer + PMCounters m_Delta ; ///< computed total delta between start/stop +}; + +#endif // CPMCDATA_H
\ No newline at end of file diff --git a/public/tier0/pointeroverride.asm b/public/tier0/pointeroverride.asm new file mode 100644 index 0000000..e75ed65 --- /dev/null +++ b/public/tier0/pointeroverride.asm @@ -0,0 +1,17 @@ +.model flat, C + +.data + __imp__EncodePointer@4 dd dummy + __imp__DecodePointer@4 dd dummy + + EXTERNDEF __imp__EncodePointer@4 : DWORD + EXTERNDEF __imp__DecodePointer@4 : DWORD + +.code + dummy proc + mov eax, [esp+4] + ret 4 + dummy endp + +end +
\ No newline at end of file diff --git a/public/tier0/progressbar.h b/public/tier0/progressbar.h new file mode 100644 index 0000000..ce28afe --- /dev/null +++ b/public/tier0/progressbar.h @@ -0,0 +1,23 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: Provide a shared place for library fucntions to report progress % for display +// +//=============================================================================// + +#ifndef PROGRESSBAR_H +#define PROGRESSBAR_H +#ifdef _WIN32 +#pragma once +#endif + + +PLATFORM_INTERFACE void ReportProgress(char const *job_name, int total_units_to_do, + int n_units_completed); + +typedef void (*ProgressReportHandler_t)( char const*, int, int ); + +// install your own handler. returns previous handler +PLATFORM_INTERFACE ProgressReportHandler_t InstallProgressReportHandler( ProgressReportHandler_t pfn); + + +#endif diff --git a/public/tier0/protected_things.h b/public/tier0/protected_things.h new file mode 100644 index 0000000..8cbfa92 --- /dev/null +++ b/public/tier0/protected_things.h @@ -0,0 +1,271 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +//=============================================================================// + +#ifndef PROTECTED_THINGS_H +#define PROTECTED_THINGS_H +#ifdef _WIN32 +#pragma once +#endif + + +// This header tries to prevent people from using potentially dangerous functions +// (like the notorious non-null-terminating strncpy) and functions that will break +// VCR mode (like time, input, registry, etc). +// +// This header should be included by ALL of our source code. + +// Eventually, ALL of these should be protected, but one man can only accomplish so much in +// one day AND work on features too! +#if defined( PROTECTED_STRINGS_ENABLE ) && !defined(DISABLE_PROTECTED_STRINGS) + + #if defined( printf ) + #undef printf + #endif + #define printf printf__HEY_YOU__USE_VSTDLIB + + #if defined( wprintf ) + #undef wprintf + #endif + #define wprintf wprintf__HEY_YOU__USE_VSTDLIB + + #if defined( strcmp ) + #undef strcmp + #endif + #define strcmp strcmp__HEY_YOU__USE_VSTDLIB + + #if defined( wcscmp ) + #undef wcscmp + #endif + #define wcscmp wcscmp__HEY_YOU__USE_VSTDLIB + + #if defined( strncpy ) + #undef strncpy + #endif + #define strncpy strncpy__HEY_YOU__USE_VSTDLIB + + #if defined( wcsncpy ) + #undef wcsncpy + #endif + #define wcsncpy wcsncpy__HEY_YOU__USE_VSTDLIB + + #if defined( strlen ) + #undef strlen + #endif + #define strlen strlen__HEY_YOU__USE_VSTDLIB + + #if defined( wcslen ) + #undef wcslen + #endif + #define wcslen wcslen__HEY_YOU__USE_VSTDLIB + + #if defined( Q_strlen ) + #undef Q_strlen + #endif + #define Q_strlen Q_strlen__HEY_YOU__USE_VSTDLIB + + #if defined( _snprintf ) + #undef _snprintf + #endif + #define _snprintf snprintf__HEY_YOU__USE_VSTDLIB + + #if defined( _snwprintf ) + #undef _snwprintf + #endif + #define _snwprintf snwprintf__HEY_YOU__USE_VSTDLIB + + #if defined( sprintf ) + #undef sprintf + #endif + #define sprintf sprintf__HEY_YOU__USE_VSTDLIB + + #if defined( swprintf ) + #undef swprintf + #endif + #define swprintf swprintf__HEY_YOU__USE_VSTDLIB + + #if defined( vsprintf ) + #undef vsprintf + #endif + #define vsprintf vsprintf__HEY_YOU__USE_VSTDLIB + + #if defined( vswprintf ) + #undef vswprintf + #endif + #define vswprintf vswprintf__HEY_YOU__USE_VSTDLIB + + #if defined( _vsnprintf ) + #undef _vsnprintf + #endif + #define _vsnprintf vsnprintf__HEY_YOU__USE_VSTDLIB + + #if defined( _vsnwprintf ) + #undef _vsnwprintf + #endif + #define _vsnwprintf vsnwprintf__HEY_YOU__USE_VSTDLIB + + #if defined( strcat ) + #undef strcat + #endif + #define strcat strcat__HEY_YOU__USE_VSTDLIB + + #if defined( wcscat ) + #undef wcscat + #endif + #define wcscat wcscat__HEY_YOU__USE_VSTDLIB + + #if defined( strncat ) + #undef strncat + #endif + #define strncat strncat__HEY_YOU__USE_VSTDLIB + + #if defined( wcsncat ) + #undef wcsncat + #endif + #define wcsncat wcsncat__HEY_YOU__USE_VSTDLIB + +#endif + + +#if defined( PROTECTED_THINGS_ENABLE ) && !defined( _X360 ) && !defined(DISABLE_PROTECTED_THINGS) + + #if defined( GetTickCount ) + #undef GetTickCount + #endif + #define GetTickCount GetTickCount__USE_VCR_MODE + + + #if defined( timeGetTime ) + #undef timeGetTime + #endif + #define timeGetTime timeGetTime__USE_VCR_MODE + #if defined( clock ) + #undef clock + #endif + #define time time__USE_VCR_MODE + + + #if defined( recvfrom ) + #undef recvfrom + #endif + #define recvfrom recvfrom__USE_VCR_MODE + + + #if defined( GetCursorPos ) + #undef GetCursorPos + #endif + #define GetCursorPos GetCursorPos__USE_VCR_MODE + + + #if defined( ScreenToClient ) + #undef ScreenToClient + #endif + #define ScreenToClient ScreenToClient__USE_VCR_MODE + + + #if defined( GetCommandLine ) + #undef GetCommandLine + #endif + #define GetCommandLine GetCommandLine__USE_VCR_MODE + + + #if defined( RegOpenKeyEx ) + #undef RegOpenKeyEx + #endif + #define RegOpenKeyEx RegOpenKeyEx__USE_VCR_MODE + + + #if defined( RegOpenKey ) + #undef RegOpenKey + #endif + #define RegOpenKey RegOpenKey__USE_VCR_MODE + + + #if defined( RegSetValueEx ) + #undef RegSetValueEx + #endif + #define RegSetValueEx RegSetValueEx__USE_VCR_MODE + + + #if defined( RegSetValue ) + #undef RegSetValue + #endif + #define RegSetValue RegSetValue__USE_VCR_MODE + + + #if defined( RegQueryValueEx ) + #undef RegQueryValueEx + #endif + #define RegQueryValueEx RegQueryValueEx__USE_VCR_MODE + + + #if defined( RegQueryValue ) + #undef RegQueryValue + #endif + #define RegQueryValue RegQueryValue__USE_VCR_MODE + + + #if defined( RegCreateKeyEx ) + #undef RegCreateKeyEx + #endif + #define RegCreateKeyEx RegCreateKeyEx__USE_VCR_MODE + + + #if defined( RegCreateKey ) + #undef RegCreateKey + #endif + #define RegCreateKey RegCreateKey__USE_VCR_MODE + + + #if defined( RegCloseKey ) + #undef RegCloseKey + #endif + #define RegCloseKey RegCloseKey__USE_VCR_MODE + + + #if defined( GetNumberOfConsoleInputEvents ) + #undef GetNumberOfConsoleInputEvents + #endif + #define GetNumberOfConsoleInputEvents GetNumberOfConsoleInputEvents__USE_VCR_MODE + + + #if defined( ReadConsoleInput ) + #undef ReadConsoleInput + #endif + #define ReadConsoleInput ReadConsoleInput__USE_VCR_MODE + + + #if defined( GetAsyncKeyState ) + #undef GetAsyncKeyState + #endif + #define GetAsyncKeyState GetAsyncKeyState__USE_VCR_MODE + + + #if defined( GetKeyState ) + #undef GetKeyState + #endif + #define GetKeyState GetKeyState__USE_VCR_MODE + + + #if defined( CreateThread ) + #undef CreateThread + #endif + #define CreateThread CreateThread__USE_VCR_MODE + + #if defined( WaitForSingleObject ) + #undef WaitForSingleObject + #endif + #define WaitForSingleObject WaitForSingleObject__USE_VCR_MODE + + #if defined( EnterCriticalSection ) + #undef EnterCriticalSection + #endif + #define EnterCriticalSection EnterCriticalSection__USE_VCR_MODE + +#endif + + +#endif // PROTECTED_THINGS_H diff --git a/public/tier0/stacktools.h b/public/tier0/stacktools.h new file mode 100644 index 0000000..c356b8c --- /dev/null +++ b/public/tier0/stacktools.h @@ -0,0 +1,153 @@ +//========= Copyright � 1996-2008, Valve Corporation, All rights reserved. ============// +// +// Purpose: Tools for grabbing/dumping the stack at runtime +// +// $NoKeywords: $ +//=============================================================================// + +#ifndef TIER0_STACKTOOLS_H +#define TIER0_STACKTOOLS_H + +#ifdef _WIN32 +#pragma once +#endif + +#include "tier0/platform.h" + +#if (defined( PLATFORM_WINDOWS ) || defined( PLATFORM_X360 )) && !defined( STEAM ) && !defined( _CERT ) && defined( TCHAR_IS_CHAR ) //designed for windows/x360, not built/tested with wide characters, not intended for release builds (but probably wouldn't damage anything) +# define ENABLE_RUNTIME_STACK_TRANSLATION //uncomment to enable runtime stack translation tools. All of which use on-demand loading of necessary dll's and pdb's +#endif + +#if defined( ENABLE_RUNTIME_STACK_TRANSLATION ) +//#define ENABLE_THREAD_PARENT_STACK_TRACING 1 //uncomment to actually enable tracking stack traces from threads and jobs to their parent thread. Must also define THREAD_PARENT_STACK_TRACE_SUPPORTED in threadtools.h +# if defined( ENABLE_THREAD_PARENT_STACK_TRACING ) +# define THREAD_PARENT_STACK_TRACE_LENGTH 32 +# endif +#endif + + + + +PLATFORM_INTERFACE int GetCallStack( void **pReturnAddressesOut, int iArrayCount, int iSkipCount ); + +//ONLY WORKS IF THE CRAWLED PORTION OF THE STACK DISABLES FRAME POINTER OMISSION (/Oy-) "vpc /nofpo" +PLATFORM_INTERFACE int GetCallStack_Fast( void **pReturnAddressesOut, int iArrayCount, int iSkipCount ); + +typedef int (*FN_GetCallStack)( void **pReturnAddressesOut, int iArrayCount, int iSkipCount ); + +//where we'll find our PDB's for win32. +PLATFORM_INTERFACE void SetStackTranslationSymbolSearchPath( const char *szSemicolonSeparatedList = NULL ); +PLATFORM_INTERFACE void StackToolsNotify_LoadedLibrary( const char *szLibName ); + +//maximum output sample "tier0.dll!TranslateStackInfo - u:\Dev\L4D\src\tier0\stacktools.cpp(162) + 4 bytes" +enum TranslateStackInfo_StyleFlags_t +{ + TSISTYLEFLAG_NONE = 0, + TSISTYLEFLAG_MODULENAME = (1<<0), //start with module Sample: "tier0.dll!" + TSISTYLEFLAG_SYMBOLNAME = (1<<1), //include the symbol name Sample: "TranslateStackInfo" + TSISTYLEFLAG_FULLPATH = (1<<2), //include full path Sample: "u:\Dev\L4D\src\tier0\stacktools.cpp" + TSISTYLEFLAG_SHORTPATH = (1<<3), //only include 2 directories Sample: "\src\tier0\stacktools.cpp" + TSISTYLEFLAG_LINE = (1<<4), //file line number Sample: "(162)" + TSISTYLEFLAG_LINEANDOFFSET = (1<<5), //file line + offset Sample: "(162) + 4 bytes" + TSISTYLEFLAG_LAST = TSISTYLEFLAG_LINEANDOFFSET, + TSISTYLEFLAG_DEFAULT = (TSISTYLEFLAG_MODULENAME | TSISTYLEFLAG_SYMBOLNAME | TSISTYLEFLAG_FULLPATH | TSISTYLEFLAG_LINEANDOFFSET), //produces sample above +}; + +//Generates a formatted list of function information, returns number of translated entries +//On 360 this generates a string that can be decoded by VXConsole in print functions. Optimal path for translation because it's one way. Other paths require multiple transactions. +PLATFORM_INTERFACE int TranslateStackInfo( const void * const *pCallStack, int iCallStackCount, tchar *szOutput, int iOutBufferSize, const tchar *szEntrySeparator, TranslateStackInfo_StyleFlags_t style = TSISTYLEFLAG_DEFAULT ); + +PLATFORM_INTERFACE void PreloadStackInformation( void * const *pAddresses, int iAddressCount ); //caches data and reduces communication with VXConsole to speed up 360 decoding when using any of the Get***FromAddress() functions. Nop on PC. +PLATFORM_INTERFACE bool GetFileAndLineFromAddress( const void *pAddress, tchar *pFileNameOut, int iMaxFileNameLength, uint32 &iLineNumberOut, uint32 *pDisplacementOut = NULL ); +PLATFORM_INTERFACE bool GetSymbolNameFromAddress( const void *pAddress, tchar *pSymbolNameOut, int iMaxSymbolNameLength, uint64 *pDisplacementOut = NULL ); +PLATFORM_INTERFACE bool GetModuleNameFromAddress( const void *pAddress, tchar *pModuleNameOut, int iMaxModuleNameLength ); + + + +class PLATFORM_CLASS CCallStackStorage //a helper class to grab a stack trace as close to the leaf code surface as possible, then pass it on to deeper functions intact with less unpredictable inlining pollution +{ +public: + CCallStackStorage( FN_GetCallStack GetStackFunction = GetCallStack, uint32 iSkipCalls = 0 ); + CCallStackStorage( const CCallStackStorage ©From ) + { + iValidEntries = copyFrom.iValidEntries; + memcpy( pStack, copyFrom.pStack, sizeof( void * ) * copyFrom.iValidEntries ); + } + + void *pStack[128]; //probably too big, possibly too small for some applications. Don't want to spend the time figuring out how to generalize this without templatizing pollution or mallocs + uint32 iValidEntries; +}; + + +//Hold onto one of these to denote the top of a functional stack trace. Also allows us to string together threads to their parents +class PLATFORM_CLASS CStackTop_Base +{ +protected: +#if defined( ENABLE_RUNTIME_STACK_TRANSLATION ) + CStackTop_Base *m_pPrevTop; + void *m_pStackBase; + void *m_pReplaceAddress; + + void * const *m_pParentStackTrace; + int m_iParentStackTraceLength; +#endif +}; + +//makes a copy of the parent stack +class PLATFORM_CLASS CStackTop_CopyParentStack : public CStackTop_Base +{ +public: + CStackTop_CopyParentStack( void * const * pParentStackTrace, int iParentStackTraceLength ); + ~CStackTop_CopyParentStack( void ); +}; + +//just references the parent stack. Assuming that you'll keep that memory around as long as you're keeping this Stack Top marker. +class PLATFORM_CLASS CStackTop_ReferenceParentStack : public CStackTop_Base +{ +public: + CStackTop_ReferenceParentStack( void * const * pParentStackTrace = NULL, int iParentStackTraceLength = 0 ); + ~CStackTop_ReferenceParentStack( void ); + void ReleaseParentStackReferences( void ); //in case you need to delete the parent stack trace before this class goes out of scope +}; + + +//Encodes data so that every byte's most significant bit is a 1. Ensuring no null terminators. +//This puts the encoded data in the 128-255 value range. Leaving all standard ascii characters for control. +//Returns string length (not including the written null terminator as is standard). +//Or if the buffer is too small. Returns negative of necessary buffer size (including room needed for null terminator) +PLATFORM_INTERFACE int EncodeBinaryToString( const void *pToEncode, int iDataLength, char *pEncodeOut, int iEncodeBufferSize ); + +//Decodes a string produced by EncodeBinaryToString(). Safe to decode in place if you don't mind trashing your string, binary byte count always less than string byte count. +//Returns: +// >= 0 is the decoded data size +// INT_MIN (most negative value possible) indicates an improperly formatted string (not our data) +// all other negative values are the negative of how much dest buffer size is necessary. +PLATFORM_INTERFACE int DecodeBinaryFromString( const char *pString, void *pDestBuffer, int iDestBufferSize, char **ppParseFinishOut = NULL ); + + + + +// 360<->VXConsole specific communication definitions +#define XBX_CALLSTACKDECODEPREFIX ":CSDECODE[" + +enum StackTranslation_BinaryHandler_Command_t +{ + ST_BHC_LOADEDLIBARY, + ST_BHC_GETTRANSLATIONINFO, +}; + +#pragma pack(push) +#pragma pack(1) +struct FullStackInfo_t +{ + const void *pAddress; + char szModuleName[24]; + char szFileName[MAX_PATH/2]; + char szSymbol[64]; + uint32 iLine; + uint32 iSymbolOffset; + +}; +#pragma pack(pop) + +#endif //#ifndef TIER0_STACKTOOLS_H diff --git a/public/tier0/systeminformation.h b/public/tier0/systeminformation.h new file mode 100644 index 0000000..f9ba59e --- /dev/null +++ b/public/tier0/systeminformation.h @@ -0,0 +1,56 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +// +//=============================================================================// + +#ifndef SYSTEMINFORMATION_H +#define SYSTEMINFORMATION_H + +#ifdef _WIN32 + #pragma once +#endif + +#ifndef PLATFORM_INTERFACE + #define PLATFORM_INTERFACE +#endif + +// +// Defines a possible outcome of a system call +// +enum SYSTEM_CALL_RESULT_t +{ + SYSCALL_SUCCESS = 0, // System call succeeded + SYSCALL_FAILED = 1, // System call failed + SYSCALL_NOPROC = 2, // Failed to find required system procedure + SYSCALL_NODLL = 3, // Failed to find or load required system module + SYSCALL_UNSUPPORTED = 4, // System call unsupported on the OS +}; + + +// +// Information about paged pool memory +// +struct PAGED_POOL_INFO_t +{ + unsigned long numPagesUsed; // Number of Paged Pool pages used + unsigned long numPagesFree; // Number of Paged Pool pages free +}; + +// +// Plat_GetMemPageSize +// Returns the size of a memory page in kilobytes. +// +PLATFORM_INTERFACE unsigned long Plat_GetMemPageSize(); + +// +// Plat_GetPagedPoolInfo +// Fills in the paged pool info structure if successful. +// +PLATFORM_INTERFACE SYSTEM_CALL_RESULT_t Plat_GetPagedPoolInfo( PAGED_POOL_INFO_t *pPPI ); + + + +#endif // #ifndef SYSTEMINFORMATION_H diff --git a/public/tier0/testthread.h b/public/tier0/testthread.h new file mode 100644 index 0000000..6ffda65 --- /dev/null +++ b/public/tier0/testthread.h @@ -0,0 +1,60 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: exposes testing thread functions +// +//============================================================================= + +#ifndef TESTTHREAD_H +#define TESTTHREAD_H +#ifdef _WIN32 +#pragma once +#endif + +#include "tier0/dbg.h" + +// test callback +typedef void (STDCALL *TestFunc)(void *pv); + +// runs the test function +DBG_INTERFACE void Test_RunTest(TestFunc func, void *pvArg); + +// call to give the test thread a chance to run +// calling thread will block until the test thread yields +// doesn't do anything if no tests are running +DBG_INTERFACE void Test_RunFrame(); + +// true if any tests are running, or have ran +DBG_INTERFACE bool Test_IsActive(); + +// sets that the test has failed +DBG_INTERFACE void Test_SetFailed(); + +// true if any tests have failed, due to an assert, warning, or explicit fail +DBG_INTERFACE bool Test_HasFailed(); + +// true if any tests have completed +DBG_INTERFACE bool Test_HasFinished(); + +// terminates the test thread +DBG_INTERFACE void Test_TerminateThread(); + +// the following functions should only be called from the test thread + +// yields to the main thread for a single frame +// passing in is a count of the number of frames that have been yielded by this yield macro +// can be used to assert if a test thread is blocked foor +DBG_INTERFACE void TestThread_Yield(); + +// utility functions to pause the test frame until the selected condition is true +#define YIELD_UNTIL(x) { int iYieldCount = 0; while (!(x)) { TestThread_Yield(); iYieldCount++; if ( iYieldCount >= 100 ) { AssertMsg( false, #x ); break; } } } + +// use this like a while(1) loop, with break; to stop yielding +#define YIELD_UNTIL_BREAK() for (; true; TestThread_Yield()) + +// yields for a single frame +#define YIELD_FRAME() { TestThread_Yield(); } +#define YIELD_TWO_FRAMES() { TestThread_Yield(); TestThread_Yield(); } + + + +#endif // TESTTHREAD_H diff --git a/public/tier0/threadtools.h b/public/tier0/threadtools.h new file mode 100644 index 0000000..b0b9b1d --- /dev/null +++ b/public/tier0/threadtools.h @@ -0,0 +1,1825 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: A collection of utility classes to simplify thread handling, and +// as much as possible contain portability problems. Here avoiding +// including windows.h. +// +//============================================================================= + +#ifndef THREADTOOLS_H +#define THREADTOOLS_H + +#include "tier0/type_traits.h" + +#include <limits.h> + +#include "tier0/platform.h" +#include "tier0/dbg.h" +#include "tier0/vcrmode.h" +#include "tier0/vprof_telemetry.h" + +#ifdef PLATFORM_WINDOWS_PC +#include <intrin.h> +#endif + +#ifdef POSIX +#include <pthread.h> +#include <errno.h> +#define WAIT_OBJECT_0 0 +#define WAIT_TIMEOUT 0x00000102 +#define WAIT_FAILED -1 +#define THREAD_PRIORITY_HIGHEST 2 +#endif + +#if defined( _WIN32 ) +#pragma once +#pragma warning(push) +#pragma warning(disable:4251) +#endif + +// #define THREAD_PROFILER 1 + +#ifndef _RETAIL +#define THREAD_MUTEX_TRACING_SUPPORTED +#if defined(_WIN32) && defined(_DEBUG) +#define THREAD_MUTEX_TRACING_ENABLED +#endif +#endif + +#ifdef _WIN32 +typedef void *HANDLE; +#endif + +// Start thread running - error if already running +enum ThreadPriorityEnum_t +{ +#if defined( PLATFORM_PS3 ) + TP_PRIORITY_NORMAL = 1001, + TP_PRIORITY_HIGH = 100, + TP_PRIORITY_LOW = 2001, + TP_PRIORITY_DEFAULT = 1001 +#error "Need PRIORITY_LOWEST/HIGHEST" +#elif defined( PLATFORM_LINUX ) + // We can use nice on Linux threads to change scheduling. + // pthreads on Linux only allows priority setting on + // real-time threads. + // NOTE: Lower numbers are higher priority, thus the need + // for TP_IS_PRIORITY_HIGHER. + TP_PRIORITY_DEFAULT = 0, + TP_PRIORITY_NORMAL = 0, + TP_PRIORITY_HIGH = -10, + TP_PRIORITY_LOW = 10, + TP_PRIORITY_HIGHEST = -20, + TP_PRIORITY_LOWEST = 19, +#else // PLATFORM_PS3 + TP_PRIORITY_DEFAULT = 0, // THREAD_PRIORITY_NORMAL + TP_PRIORITY_NORMAL = 0, // THREAD_PRIORITY_NORMAL + TP_PRIORITY_HIGH = 1, // THREAD_PRIORITY_ABOVE_NORMAL + TP_PRIORITY_LOW = -1, // THREAD_PRIORITY_BELOW_NORMAL + TP_PRIORITY_HIGHEST = 2, // THREAD_PRIORITY_HIGHEST + TP_PRIORITY_LOWEST = -2, // THREAD_PRIORITY_LOWEST +#endif // PLATFORM_PS3 +}; + +//----------------------------------------------------------------------------- +// +//----------------------------------------------------------------------------- + +const unsigned TT_INFINITE = 0xffffffff; + +#ifndef NO_THREAD_LOCAL + +#ifndef THREAD_LOCAL +#ifdef _WIN32 +#define THREAD_LOCAL __declspec(thread) +#elif POSIX +#define THREAD_LOCAL __thread +#endif +#endif + +#endif // NO_THREAD_LOCAL + +typedef unsigned long ThreadId_t; + +//----------------------------------------------------------------------------- +// +// Simple thread creation. Differs from VCR mode/CreateThread/_beginthreadex +// in that it accepts a standard C function rather than compiler specific one. +// +//----------------------------------------------------------------------------- +FORWARD_DECLARE_HANDLE( ThreadHandle_t ); +typedef unsigned (*ThreadFunc_t)( void *pParam ); + +PLATFORM_OVERLOAD ThreadHandle_t CreateSimpleThread( ThreadFunc_t, void *pParam, ThreadId_t *pID, unsigned stackSize = 0 ); +PLATFORM_INTERFACE ThreadHandle_t CreateSimpleThread( ThreadFunc_t, void *pParam, unsigned stackSize = 0 ); +PLATFORM_INTERFACE bool ReleaseThreadHandle( ThreadHandle_t ); + + +//----------------------------------------------------------------------------- + +PLATFORM_INTERFACE void ThreadSleep(unsigned duration = 0); +PLATFORM_INTERFACE uint ThreadGetCurrentId(); +PLATFORM_INTERFACE ThreadHandle_t ThreadGetCurrentHandle(); +PLATFORM_INTERFACE int ThreadGetPriority( ThreadHandle_t hThread = NULL ); +PLATFORM_INTERFACE bool ThreadSetPriority( ThreadHandle_t hThread, int priority ); +inline bool ThreadSetPriority( int priority ) { return ThreadSetPriority( NULL, priority ); } +PLATFORM_INTERFACE bool ThreadInMainThread(); +PLATFORM_INTERFACE void DeclareCurrentThreadIsMainThread(); + +// NOTE: ThreadedLoadLibraryFunc_t needs to return the sleep time in milliseconds or TT_INFINITE +typedef int (*ThreadedLoadLibraryFunc_t)(); +PLATFORM_INTERFACE void SetThreadedLoadLibraryFunc( ThreadedLoadLibraryFunc_t func ); +PLATFORM_INTERFACE ThreadedLoadLibraryFunc_t GetThreadedLoadLibraryFunc(); + +#if defined( _WIN32 ) && !defined( _WIN64 ) && !defined( _X360 ) +extern "C" unsigned long __declspec(dllimport) __stdcall GetCurrentThreadId(); +#define ThreadGetCurrentId GetCurrentThreadId +#endif + +inline void ThreadPause() +{ +#if defined( PLATFORM_WINDOWS_PC ) + // Intrinsic for __asm pause; from <intrin.h> + _mm_pause(); +#elif POSIX + __asm __volatile( "pause" ); +#elif defined( _X360 ) +#else +#error "implement me" +#endif +} + +PLATFORM_INTERFACE bool ThreadJoin( ThreadHandle_t, unsigned timeout = TT_INFINITE ); +// If you're not calling ThreadJoin, you need to call ThreadDetach so pthreads on Linux knows it can +// free the memory for this thread. Otherwise you wind up leaking threads until you run out and +// CreateSimpleThread() will fail. +PLATFORM_INTERFACE void ThreadDetach( ThreadHandle_t ); + +PLATFORM_INTERFACE void ThreadSetDebugName( ThreadId_t id, const char *pszName ); +inline void ThreadSetDebugName( const char *pszName ) { ThreadSetDebugName( (ThreadId_t)-1, pszName ); } + +PLATFORM_INTERFACE void ThreadSetAffinity( ThreadHandle_t hThread, int nAffinityMask ); + +//----------------------------------------------------------------------------- + +enum ThreadWaitResult_t +{ + TW_FAILED = 0xffffffff, // WAIT_FAILED + TW_TIMEOUT = 0x00000102, // WAIT_TIMEOUT +}; + +#ifdef _WIN32 +PLATFORM_INTERFACE int ThreadWaitForObjects( int nEvents, const HANDLE *pHandles, bool bWaitAll = true, unsigned timeout = TT_INFINITE ); +inline int ThreadWaitForObject( HANDLE handle, bool bWaitAll = true, unsigned timeout = TT_INFINITE ) { return ThreadWaitForObjects( 1, &handle, bWaitAll, timeout ); } +#endif + +//----------------------------------------------------------------------------- +// +// Interlock methods. These perform very fast atomic thread +// safe operations. These are especially relevant in a multi-core setting. +// +//----------------------------------------------------------------------------- + +#ifdef _WIN32 +#define NOINLINE +#elif POSIX +#define NOINLINE __attribute__ ((noinline)) +#endif + +// ThreadMemoryBarrier is a fence/barrier sufficient for most uses. It prevents reads +// from moving past reads, and writes moving past writes. It is sufficient for +// read-acquire and write-release barriers. It is not a full barrier and it does +// not prevent reads from moving past writes -- that would require a full __sync() +// on PPC and is significantly more expensive. +#if defined( _X360 ) || defined( _PS3 ) + #define ThreadMemoryBarrier() __lwsync() + +#elif defined(_MSC_VER) + // Prevent compiler reordering across this barrier. This is + // sufficient for most purposes on x86/x64. + + #if _MSC_VER < 1500 + // !KLUDGE! For VC 2005 + // http://connect.microsoft.com/VisualStudio/feedback/details/100051 + #pragma intrinsic(_ReadWriteBarrier) + #endif + #define ThreadMemoryBarrier() _ReadWriteBarrier() +#elif defined(GNUC) + // Prevent compiler reordering across this barrier. This is + // sufficient for most purposes on x86/x64. + // http://preshing.com/20120625/memory-ordering-at-compile-time + #define ThreadMemoryBarrier() asm volatile("" ::: "memory") +#else + #error Every platform needs to define ThreadMemoryBarrier to at least prevent compiler reordering +#endif + +#if defined(_WIN32) && !defined(_X360) + #if ( _MSC_VER >= 1310 ) + #define USE_INTRINSIC_INTERLOCKED + #endif +#endif + +#ifdef USE_INTRINSIC_INTERLOCKED +extern "C" +{ + long __cdecl _InterlockedIncrement(volatile long*); + long __cdecl _InterlockedDecrement(volatile long*); + long __cdecl _InterlockedExchange(volatile long*, long); + long __cdecl _InterlockedExchangeAdd(volatile long*, long); + long __cdecl _InterlockedCompareExchange(volatile long*, long, long); +} + +#pragma intrinsic( _InterlockedCompareExchange ) +#pragma intrinsic( _InterlockedDecrement ) +#pragma intrinsic( _InterlockedExchange ) +#pragma intrinsic( _InterlockedExchangeAdd ) +#pragma intrinsic( _InterlockedIncrement ) + +inline long ThreadInterlockedIncrement( long volatile *p ) { Assert( (size_t)p % 4 == 0 ); return _InterlockedIncrement( p ); } +inline long ThreadInterlockedDecrement( long volatile *p ) { Assert( (size_t)p % 4 == 0 ); return _InterlockedDecrement( p ); } +inline long ThreadInterlockedExchange( long volatile *p, long value ) { Assert( (size_t)p % 4 == 0 ); return _InterlockedExchange( p, value ); } +inline long ThreadInterlockedExchangeAdd( long volatile *p, long value ) { Assert( (size_t)p % 4 == 0 ); return _InterlockedExchangeAdd( p, value ); } +inline long ThreadInterlockedCompareExchange( long volatile *p, long value, long comperand ) { Assert( (size_t)p % 4 == 0 ); return _InterlockedCompareExchange( p, value, comperand ); } +inline bool ThreadInterlockedAssignIf( long volatile *p, long value, long comperand ) { Assert( (size_t)p % 4 == 0 ); return ( _InterlockedCompareExchange( p, value, comperand ) == comperand ); } +#else +PLATFORM_INTERFACE long ThreadInterlockedIncrement( long volatile * ); +PLATFORM_INTERFACE long ThreadInterlockedDecrement( long volatile * ); +PLATFORM_INTERFACE long ThreadInterlockedExchange( long volatile *, long value ); +PLATFORM_INTERFACE long ThreadInterlockedExchangeAdd( long volatile *, long value ); +PLATFORM_INTERFACE long ThreadInterlockedCompareExchange( long volatile *, long value, long comperand ); +PLATFORM_INTERFACE bool ThreadInterlockedAssignIf( long volatile *, long value, long comperand ); +#endif + +inline unsigned ThreadInterlockedExchangeSubtract( long volatile *p, long value ) { return ThreadInterlockedExchangeAdd( (long volatile *)p, -value ); } + +#if defined( USE_INTRINSIC_INTERLOCKED ) && !defined( _WIN64 ) +#define TIPTR() +inline void *ThreadInterlockedExchangePointer( void * volatile *p, void *value ) { return (void *)_InterlockedExchange( reinterpret_cast<long volatile *>(p), reinterpret_cast<long>(value) ); } +inline void *ThreadInterlockedCompareExchangePointer( void * volatile *p, void *value, void *comperand ) { return (void *)_InterlockedCompareExchange( reinterpret_cast<long volatile *>(p), reinterpret_cast<long>(value), reinterpret_cast<long>(comperand) ); } +inline bool ThreadInterlockedAssignPointerIf( void * volatile *p, void *value, void *comperand ) { return ( _InterlockedCompareExchange( reinterpret_cast<long volatile *>(p), reinterpret_cast<long>(value), reinterpret_cast<long>(comperand) ) == reinterpret_cast<long>(comperand) ); } +#else +PLATFORM_INTERFACE void *ThreadInterlockedExchangePointer( void * volatile *, void *value ) NOINLINE; +PLATFORM_INTERFACE void *ThreadInterlockedCompareExchangePointer( void * volatile *, void *value, void *comperand ) NOINLINE; +PLATFORM_INTERFACE bool ThreadInterlockedAssignPointerIf( void * volatile *, void *value, void *comperand ) NOINLINE; +#endif + +inline void const *ThreadInterlockedExchangePointerToConst( void const * volatile *p, void const *value ) { return ThreadInterlockedExchangePointer( const_cast < void * volatile * > ( p ), const_cast < void * > ( value ) ); } +inline void const *ThreadInterlockedCompareExchangePointerToConst( void const * volatile *p, void const *value, void const *comperand ) { return ThreadInterlockedCompareExchangePointer( const_cast < void * volatile * > ( p ), const_cast < void * > ( value ), const_cast < void * > ( comperand ) ); } +inline bool ThreadInterlockedAssignPointerToConstIf( void const * volatile *p, void const *value, void const *comperand ) { return ThreadInterlockedAssignPointerIf( const_cast < void * volatile * > ( p ), const_cast < void * > ( value ), const_cast < void * > ( comperand ) ); } + +#if defined( PLATFORM_64BITS ) +#if defined (_WIN32) +typedef __m128i int128; +inline int128 int128_zero() { return _mm_setzero_si128(); } +#else +typedef __int128_t int128; +#define int128_zero() 0 +#endif + +PLATFORM_INTERFACE bool ThreadInterlockedAssignIf128( volatile int128 *pDest, const int128 &value, const int128 &comperand ) NOINLINE; + +#endif + +PLATFORM_INTERFACE int64 ThreadInterlockedIncrement64( int64 volatile * ) NOINLINE; +PLATFORM_INTERFACE int64 ThreadInterlockedDecrement64( int64 volatile * ) NOINLINE; +PLATFORM_INTERFACE int64 ThreadInterlockedCompareExchange64( int64 volatile *, int64 value, int64 comperand ) NOINLINE; +PLATFORM_INTERFACE int64 ThreadInterlockedExchange64( int64 volatile *, int64 value ) NOINLINE; +PLATFORM_INTERFACE int64 ThreadInterlockedExchangeAdd64( int64 volatile *, int64 value ) NOINLINE; +PLATFORM_INTERFACE bool ThreadInterlockedAssignIf64(volatile int64 *pDest, int64 value, int64 comperand ) NOINLINE; + +inline unsigned ThreadInterlockedExchangeSubtract( unsigned volatile *p, unsigned value ) { return ThreadInterlockedExchangeAdd( (long volatile *)p, value ); } +inline unsigned ThreadInterlockedIncrement( unsigned volatile *p ) { return ThreadInterlockedIncrement( (long volatile *)p ); } +inline unsigned ThreadInterlockedDecrement( unsigned volatile *p ) { return ThreadInterlockedDecrement( (long volatile *)p ); } +inline unsigned ThreadInterlockedExchange( unsigned volatile *p, unsigned value ) { return ThreadInterlockedExchange( (long volatile *)p, value ); } +inline unsigned ThreadInterlockedExchangeAdd( unsigned volatile *p, unsigned value ) { return ThreadInterlockedExchangeAdd( (long volatile *)p, value ); } +inline unsigned ThreadInterlockedCompareExchange( unsigned volatile *p, unsigned value, unsigned comperand ) { return ThreadInterlockedCompareExchange( (long volatile *)p, value, comperand ); } +inline bool ThreadInterlockedAssignIf( unsigned volatile *p, unsigned value, unsigned comperand ) { return ThreadInterlockedAssignIf( (long volatile *)p, value, comperand ); } + +inline int ThreadInterlockedExchangeSubtract( int volatile *p, int value ) { return ThreadInterlockedExchangeAdd( (long volatile *)p, value ); } +inline int ThreadInterlockedIncrement( int volatile *p ) { return ThreadInterlockedIncrement( (long volatile *)p ); } +inline int ThreadInterlockedDecrement( int volatile *p ) { return ThreadInterlockedDecrement( (long volatile *)p ); } +inline int ThreadInterlockedExchange( int volatile *p, int value ) { return ThreadInterlockedExchange( (long volatile *)p, value ); } +inline int ThreadInterlockedExchangeAdd( int volatile *p, int value ) { return ThreadInterlockedExchangeAdd( (long volatile *)p, value ); } +inline int ThreadInterlockedCompareExchange( int volatile *p, int value, int comperand ) { return ThreadInterlockedCompareExchange( (long volatile *)p, value, comperand ); } +inline bool ThreadInterlockedAssignIf( int volatile *p, int value, int comperand ) { return ThreadInterlockedAssignIf( (long volatile *)p, value, comperand ); } + +//----------------------------------------------------------------------------- +// Access to VTune thread profiling +//----------------------------------------------------------------------------- +#if defined(_WIN32) && defined(THREAD_PROFILER) +PLATFORM_INTERFACE void ThreadNotifySyncPrepare(void *p); +PLATFORM_INTERFACE void ThreadNotifySyncCancel(void *p); +PLATFORM_INTERFACE void ThreadNotifySyncAcquired(void *p); +PLATFORM_INTERFACE void ThreadNotifySyncReleasing(void *p); +#else +#define ThreadNotifySyncPrepare(p) ((void)0) +#define ThreadNotifySyncCancel(p) ((void)0) +#define ThreadNotifySyncAcquired(p) ((void)0) +#define ThreadNotifySyncReleasing(p) ((void)0) +#endif + +//----------------------------------------------------------------------------- +// Encapsulation of a thread local datum (needed because THREAD_LOCAL doesn't +// work in a DLL loaded with LoadLibrary() +//----------------------------------------------------------------------------- + +#ifndef NO_THREAD_LOCAL + +#if defined(_LINUX) && !defined(OSX) +// linux totally supports compiler thread locals, even across dll's. +#define PLAT_COMPILER_SUPPORTED_THREADLOCALS 1 +#define CTHREADLOCALINTEGER( typ ) __thread int +#define CTHREADLOCALINT __thread int +#define CTHREADLOCALPTR( typ ) __thread typ * +#define CTHREADLOCAL( typ ) __thread typ +#define GETLOCAL( x ) ( x ) +#endif // _LINUX && !OSX + +#if defined(WIN32) || defined(OSX) +#ifndef __AFXTLS_H__ // not compatible with some Windows headers +#define CTHREADLOCALINT CThreadLocalInt<int> +#define CTHREADLOCALINTEGER( typ ) CThreadLocalInt<typ> +#define CTHREADLOCALPTR( typ ) CThreadLocalPtr<typ> +#define CTHREADLOCAL( typ ) CThreadLocal<typ> +#define GETLOCAL( x ) ( x.Get() ) +#endif +#endif // WIN32 || OSX + +#endif // NO_THREAD_LOCALS + +#ifndef __AFXTLS_H__ // not compatible with some Windows headers +#ifndef NO_THREAD_LOCAL + +class PLATFORM_CLASS CThreadLocalBase + { +public: + CThreadLocalBase(); + ~CThreadLocalBase(); + + void * Get() const; + void Set(void *); + +private: +#ifdef _WIN32 + uint32 m_index; +#elif POSIX + pthread_key_t m_index; +#endif + }; + + //--------------------------------------------------------- + +#ifndef __AFXTLS_H__ + + template <class T> + class CThreadLocal : public CThreadLocalBase + { + public: + CThreadLocal() + { + COMPILE_TIME_ASSERT( sizeof(T) == sizeof(void *) ); + } + + T Get() const + { + return reinterpret_cast<T>( CThreadLocalBase::Get() ); + } + + void Set(T val) + { + CThreadLocalBase::Set( reinterpret_cast<void *>(val) ); + } + }; + +#endif + + //--------------------------------------------------------- + +template <class T = intp> + class CThreadLocalInt : public CThreadLocal<T> + { + public: + CThreadLocalInt() + { + COMPILE_TIME_ASSERT( sizeof(T) >= sizeof(int) ); + } + + operator int() const { return (int)this->Get(); } + int operator=( int i ) { this->Set( (intp)i ); return i; } + + int operator++() { T i = this->Get(); this->Set( ++i ); return (int)i; } + int operator++(int) { T i = this->Get(); this->Set( i + 1 ); return (int)i; } + + int operator--() { T i = this->Get(); this->Set( --i ); return (int)i; } + int operator--(int) { T i = this->Get(); this->Set( i - 1 ); return (int)i; } + }; + + + //--------------------------------------------------------- + + template <class T> + class CThreadLocalPtr : private CThreadLocalBase + { + public: + CThreadLocalPtr() {} + + operator const void *() const { return (T *)Get(); } + operator void *() { return (T *)Get(); } + + operator const T *() const { return (T *)Get(); } + operator const T *() { return (T *)Get(); } + operator T *() { return (T *)Get(); } + + int operator=( int i ) { AssertMsg( i == 0, "Only NULL allowed on integer assign" ); Set( NULL ); return 0; } + T * operator=( T *p ) { Set( p ); return p; } + + bool operator !() const { return (!Get()); } + bool operator!=( int i ) const { AssertMsg( i == 0, "Only NULL allowed on integer compare" ); return (Get() != NULL); } + bool operator==( int i ) const { AssertMsg( i == 0, "Only NULL allowed on integer compare" ); return (Get() == NULL); } + bool operator==( const void *p ) const { return (Get() == p); } + bool operator!=( const void *p ) const { return (Get() != p); } + bool operator==( const T *p ) const { return operator==((void*)p); } + bool operator!=( const T *p ) const { return operator!=((void*)p); } + + T * operator->() { return (T *)Get(); } + T & operator *() { return *((T *)Get()); } + + const T * operator->() const { return (T *)Get(); } + const T & operator *() const { return *((T *)Get()); } + + const T & operator[]( int i ) const { return *((T *)Get() + i); } + T & operator[]( int i ) { return *((T *)Get() + i); } + + private: + // Disallowed operations + CThreadLocalPtr( T *pFrom ); + CThreadLocalPtr( const CThreadLocalPtr<T> &from ); + T **operator &(); + T * const *operator &() const; + void operator=( const CThreadLocalPtr<T> &from ); + bool operator==( const CThreadLocalPtr<T> &p ) const; + bool operator!=( const CThreadLocalPtr<T> &p ) const; + }; + +#endif // NO_THREAD_LOCAL +#endif // !__AFXTLS_H__ + +//----------------------------------------------------------------------------- +// +// A super-fast thread-safe integer A simple class encapsulating the notion of an +// atomic integer used across threads that uses the built in and faster +// "interlocked" functionality rather than a full-blown mutex. Useful for simple +// things like reference counts, etc. +// +//----------------------------------------------------------------------------- + +template <typename T> +class CInterlockedIntT +{ +public: + CInterlockedIntT() : m_value( 0 ) { COMPILE_TIME_ASSERT( sizeof(T) == sizeof(long) ); } + CInterlockedIntT( T value ) : m_value( value ) {} + + T GetRaw() const { return m_value; } + + operator T() const { return m_value; } + + bool operator!() const { return ( m_value == 0 ); } + bool operator==( T rhs ) const { return ( m_value == rhs ); } + bool operator!=( T rhs ) const { return ( m_value != rhs ); } + + T operator++() { return (T)ThreadInterlockedIncrement( (long *)&m_value ); } + T operator++(int) { return operator++() - 1; } + + T operator--() { return (T)ThreadInterlockedDecrement( (long *)&m_value ); } + T operator--(int) { return operator--() + 1; } + + bool AssignIf( T conditionValue, T newValue ) { return ThreadInterlockedAssignIf( (long *)&m_value, (long)newValue, (long)conditionValue ); } + + T operator=( T newValue ) { ThreadInterlockedExchange((long *)&m_value, newValue); return m_value; } + + void operator+=( T add ) { ThreadInterlockedExchangeAdd( (long *)&m_value, (long)add ); } + void operator-=( T subtract ) { operator+=( -subtract ); } + void operator*=( T multiplier ) { + T original, result; + do + { + original = m_value; + result = original * multiplier; + } while ( !AssignIf( original, result ) ); + } + void operator/=( T divisor ) { + T original, result; + do + { + original = m_value; + result = original / divisor; + } while ( !AssignIf( original, result ) ); + } + + T operator+( T rhs ) const { return m_value + rhs; } + T operator-( T rhs ) const { return m_value - rhs; } + +private: + volatile T m_value; +}; + +typedef CInterlockedIntT<int> CInterlockedInt; +typedef CInterlockedIntT<unsigned> CInterlockedUInt; + +//----------------------------------------------------------------------------- + +template <typename T> +class CInterlockedPtr +{ +public: + CInterlockedPtr() : m_value( 0 ) {} + CInterlockedPtr( T *value ) : m_value( value ) {} + + operator T *() const { return m_value; } + + bool operator!() const { return ( m_value == 0 ); } + bool operator==( T *rhs ) const { return ( m_value == rhs ); } + bool operator!=( T *rhs ) const { return ( m_value != rhs ); } + +#if defined( PLATFORM_64BITS ) + T *operator++() { return ((T *)ThreadInterlockedExchangeAdd64( (int64 *)&m_value, sizeof(T) )) + 1; } + T *operator++(int) { return (T *)ThreadInterlockedExchangeAdd64( (int64 *)&m_value, sizeof(T) ); } + + T *operator--() { return ((T *)ThreadInterlockedExchangeAdd64( (int64 *)&m_value, -sizeof(T) )) - 1; } + T *operator--(int) { return (T *)ThreadInterlockedExchangeAdd64( (int64 *)&m_value, -sizeof(T) ); } + + bool AssignIf( T *conditionValue, T *newValue ) { return ThreadInterlockedAssignPointerToConstIf( (void const **) &m_value, (void const *) newValue, (void const *) conditionValue ); } + + T *operator=( T *newValue ) { ThreadInterlockedExchangePointerToConst( (void const **) &m_value, (void const *) newValue ); return newValue; } + + void operator+=( int add ) { ThreadInterlockedExchangeAdd64( (int64 *)&m_value, add * sizeof(T) ); } +#else + T *operator++() { return ((T *)ThreadInterlockedExchangeAdd( (long *)&m_value, sizeof(T) )) + 1; } + T *operator++(int) { return (T *)ThreadInterlockedExchangeAdd( (long *)&m_value, sizeof(T) ); } + + T *operator--() { return ((T *)ThreadInterlockedExchangeAdd( (long *)&m_value, -sizeof(T) )) - 1; } + T *operator--(int) { return (T *)ThreadInterlockedExchangeAdd( (long *)&m_value, -sizeof(T) ); } + + bool AssignIf( T *conditionValue, T *newValue ) { return ThreadInterlockedAssignPointerToConstIf( (void const **) &m_value, (void const *) newValue, (void const *) conditionValue ); } + + T *operator=( T *newValue ) { ThreadInterlockedExchangePointerToConst( (void const **) &m_value, (void const *) newValue ); return newValue; } + + void operator+=( int add ) { ThreadInterlockedExchangeAdd( (long *)&m_value, add * sizeof(T) ); } +#endif + + void operator-=( int subtract ) { operator+=( -subtract ); } + + T *operator+( int rhs ) const { return m_value + rhs; } + T *operator-( int rhs ) const { return m_value - rhs; } + T *operator+( unsigned rhs ) const { return m_value + rhs; } + T *operator-( unsigned rhs ) const { return m_value - rhs; } + size_t operator-( T *p ) const { return m_value - p; } + size_t operator-( const CInterlockedPtr<T> &p ) const { return m_value - p.m_value; } + +private: + T * volatile m_value; +}; + +//----------------------------------------------------------------------------- +// +// Platform independent verification that multiple threads aren't getting into the same code at the same time. +// Note: This is intended for use to identify problems, it doesn't provide any sort of thread safety. +// +//----------------------------------------------------------------------------- +class ReentrancyVerifier +{ +public: + inline ReentrancyVerifier(CInterlockedInt* counter, int sleepTimeMS) + : mCounter(counter) + { + Assert(mCounter != NULL); + + if (++(*mCounter) != 1) { + DebuggerBreakIfDebugging_StagingOnly(); + } + + if (sleepTimeMS > 0) + { + ThreadSleep(sleepTimeMS); + } + } + + inline ~ReentrancyVerifier() + { + if (--(*mCounter) != 0) { + DebuggerBreakIfDebugging_StagingOnly(); + } + } + +private: + CInterlockedInt* mCounter; +}; + + +//----------------------------------------------------------------------------- +// +// Platform independent for critical sections management +// +//----------------------------------------------------------------------------- + +class PLATFORM_CLASS CThreadMutex +{ +public: + CThreadMutex(); + ~CThreadMutex(); + + //------------------------------------------------------ + // Mutex acquisition/release. Const intentionally defeated. + //------------------------------------------------------ + void Lock(); + void Lock() const { (const_cast<CThreadMutex *>(this))->Lock(); } + void Unlock(); + void Unlock() const { (const_cast<CThreadMutex *>(this))->Unlock(); } + + bool TryLock(); + bool TryLock() const { return (const_cast<CThreadMutex *>(this))->TryLock(); } + + //------------------------------------------------------ + // Use this to make deadlocks easier to track by asserting + // when it is expected that the current thread owns the mutex + //------------------------------------------------------ + bool AssertOwnedByCurrentThread(); + + //------------------------------------------------------ + // Enable tracing to track deadlock problems + //------------------------------------------------------ + void SetTrace( bool ); + +private: + // Disallow copying + CThreadMutex( const CThreadMutex & ); + CThreadMutex &operator=( const CThreadMutex & ); + +#if defined( _WIN32 ) + // Efficient solution to breaking the windows.h dependency, invariant is tested. +#ifdef _WIN64 + #define TT_SIZEOF_CRITICALSECTION 40 +#else +#ifndef _X360 + #define TT_SIZEOF_CRITICALSECTION 24 +#else + #define TT_SIZEOF_CRITICALSECTION 28 +#endif // !_XBOX +#endif // _WIN64 + byte m_CriticalSection[TT_SIZEOF_CRITICALSECTION]; +#elif defined(POSIX) + pthread_mutex_t m_Mutex; + pthread_mutexattr_t m_Attr; +#else +#error +#endif + +#ifdef THREAD_MUTEX_TRACING_SUPPORTED + // Debugging (always here to allow mixed debug/release builds w/o changing size) + uint m_currentOwnerID; + uint16 m_lockCount; + bool m_bTrace; +#endif +}; + +//----------------------------------------------------------------------------- +// +// An alternative mutex that is useful for cases when thread contention is +// rare, but a mutex is required. Instances should be declared volatile. +// Sleep of 0 may not be sufficient to keep high priority threads from starving +// lesser threads. This class is not a suitable replacement for a critical +// section if the resource contention is high. +// +//----------------------------------------------------------------------------- + +#if !defined(THREAD_PROFILER) + +class CThreadFastMutex +{ +public: + CThreadFastMutex() + : m_ownerID( 0 ), + m_depth( 0 ) + { + } + +private: + FORCEINLINE bool TryLockInline( const uint32 threadId ) volatile + { + if ( threadId != m_ownerID && !ThreadInterlockedAssignIf( (volatile long *)&m_ownerID, (long)threadId, 0 ) ) + return false; + + ThreadMemoryBarrier(); + ++m_depth; + return true; + } + + bool TryLock( const uint32 threadId ) volatile + { + return TryLockInline( threadId ); + } + + PLATFORM_CLASS void Lock( const uint32 threadId, unsigned nSpinSleepTime ) volatile; + +public: + bool TryLock() volatile + { +#ifdef _DEBUG + if ( m_depth == INT_MAX ) + DebuggerBreak(); + + if ( m_depth < 0 ) + DebuggerBreak(); +#endif + return TryLockInline( ThreadGetCurrentId() ); + } + +#ifndef _DEBUG + FORCEINLINE +#endif + void Lock( unsigned int nSpinSleepTime = 0 ) volatile + { + const uint32 threadId = ThreadGetCurrentId(); + + if ( !TryLockInline( threadId ) ) + { + ThreadPause(); + Lock( threadId, nSpinSleepTime ); + } +#ifdef _DEBUG + if ( m_ownerID != ThreadGetCurrentId() ) + DebuggerBreak(); + + if ( m_depth == INT_MAX ) + DebuggerBreak(); + + if ( m_depth < 0 ) + DebuggerBreak(); +#endif + } + +#ifndef _DEBUG + FORCEINLINE +#endif + void Unlock() volatile + { +#ifdef _DEBUG + if ( m_ownerID != ThreadGetCurrentId() ) + DebuggerBreak(); + + if ( m_depth <= 0 ) + DebuggerBreak(); +#endif + + --m_depth; + if ( !m_depth ) + { + ThreadMemoryBarrier(); + ThreadInterlockedExchange( &m_ownerID, 0 ); + } + } + +#ifdef WIN32 + bool TryLock() const volatile { return (const_cast<CThreadFastMutex *>(this))->TryLock(); } + void Lock(unsigned nSpinSleepTime = 1 ) const volatile { (const_cast<CThreadFastMutex *>(this))->Lock( nSpinSleepTime ); } + void Unlock() const volatile { (const_cast<CThreadFastMutex *>(this))->Unlock(); } +#endif + // To match regular CThreadMutex: + bool AssertOwnedByCurrentThread() { return true; } + void SetTrace( bool ) {} + + uint32 GetOwnerId() const { return m_ownerID; } + int GetDepth() const { return m_depth; } +private: + volatile uint32 m_ownerID; + int m_depth; +}; + +#ifdef COMPILER_CLANG +# pragma clang diagnostic push +# pragma clang diagnostic ignored "-Wunused-private-field" +#endif // Q_CC_CLANG + +class ALIGN128 CAlignedThreadFastMutex : public CThreadFastMutex +{ +public: + CAlignedThreadFastMutex() + { + Assert( (size_t)this % 128 == 0 && sizeof(*this) == 128 ); + } + +private: + uint8 pad[128-sizeof(CThreadFastMutex)]; +} ALIGN128_POST; + +#ifdef COMPILER_CLANG +# pragma clang diagnostic pop +#endif + +#else +typedef CThreadMutex CThreadFastMutex; +#endif + +//----------------------------------------------------------------------------- +// +//----------------------------------------------------------------------------- + +class CThreadNullMutex +{ +public: + static void Lock() {} + static void Unlock() {} + + static bool TryLock() { return true; } + static bool AssertOwnedByCurrentThread() { return true; } + static void SetTrace( bool b ) {} + + static uint32 GetOwnerId() { return 0; } + static int GetDepth() { return 0; } +}; + +//----------------------------------------------------------------------------- +// +// A mutex decorator class used to control the use of a mutex, to make it +// less expensive when not multithreading +// +//----------------------------------------------------------------------------- + +template <class BaseClass, bool *pCondition> +class CThreadConditionalMutex : public BaseClass +{ +public: + void Lock() { if ( *pCondition ) BaseClass::Lock(); } + void Lock() const { if ( *pCondition ) BaseClass::Lock(); } + void Unlock() { if ( *pCondition ) BaseClass::Unlock(); } + void Unlock() const { if ( *pCondition ) BaseClass::Unlock(); } + + bool TryLock() { if ( *pCondition ) return BaseClass::TryLock(); else return true; } + bool TryLock() const { if ( *pCondition ) return BaseClass::TryLock(); else return true; } + bool AssertOwnedByCurrentThread() { if ( *pCondition ) return BaseClass::AssertOwnedByCurrentThread(); else return true; } + void SetTrace( bool b ) { if ( *pCondition ) BaseClass::SetTrace( b ); } +}; + +//----------------------------------------------------------------------------- +// Mutex decorator that blows up if another thread enters +//----------------------------------------------------------------------------- + +template <class BaseClass> +class CThreadTerminalMutex : public BaseClass +{ +public: + bool TryLock() { if ( !BaseClass::TryLock() ) { DebuggerBreak(); return false; } return true; } + bool TryLock() const { if ( !BaseClass::TryLock() ) { DebuggerBreak(); return false; } return true; } + void Lock() { if ( !TryLock() ) BaseClass::Lock(); } + void Lock() const { if ( !TryLock() ) BaseClass::Lock(); } + +}; + +//----------------------------------------------------------------------------- +// +// Class to Lock a critical section, and unlock it automatically +// when the lock goes out of scope +// +//----------------------------------------------------------------------------- + +template <class MUTEX_TYPE = CThreadMutex> +class CAutoLockT +{ +public: + FORCEINLINE CAutoLockT( MUTEX_TYPE &lock, const char* pMutexName, const char* pFilename, int nLineNum, uint64 minReportDurationUs ) + : m_lock( const_cast< typename V_remove_const< MUTEX_TYPE >::type & >( lock ) ) + , m_pMutexName( pMutexName ) + , m_pFilename( pFilename ) + , m_nLineNum( nLineNum ) + , m_bOwned( true ) + { + tmTryLockEx( TELEMETRY_LEVEL0, &m_uLockMatcher, minReportDurationUs, pFilename, nLineNum, &m_lock, pMutexName ); + m_lock.Lock(); + tmEndTryLockEx( TELEMETRY_LEVEL0, m_uLockMatcher, pFilename, nLineNum, &m_lock, TMLR_SUCCESS ); + tmSetLockStateEx( TELEMETRY_LEVEL0, pFilename, nLineNum, &m_lock, TMLS_LOCKED, pMutexName ); + } + + FORCEINLINE CAutoLockT<MUTEX_TYPE>( CAutoLockT<MUTEX_TYPE> && rhs ) + : m_lock( const_cast< typename V_remove_const< MUTEX_TYPE >::type &>( rhs.m_lock ) ) + { + m_pMutexName = rhs.m_pMutexName; + m_pFilename = rhs.m_pFilename; + m_nLineNum = rhs.m_nLineNum; + #ifdef RAD_TELEMETRY_ENABLED + m_uLockMatcher = rhs.m_uLockMatcher; + #endif + m_bOwned = true; + rhs.m_bOwned = false; + } + + FORCEINLINE ~CAutoLockT() + { + if ( m_bOwned ) + { + m_lock.Unlock(); + tmSetLockStateEx( TELEMETRY_LEVEL0, m_pFilename, m_nLineNum, &m_lock, TMLS_RELEASED, m_pMutexName ); + } + } + +private: + typename V_remove_const< MUTEX_TYPE >::type &m_lock; + const char* m_pMutexName; + const char* m_pFilename; + int m_nLineNum; + bool m_bOwned; // Did owenership of the lock pass to another instance? + +#ifdef RAD_TELEMETRY_ENABLED + TmU64 m_uLockMatcher; +#endif + + // Disallow copying + CAutoLockT<MUTEX_TYPE>( const CAutoLockT<MUTEX_TYPE> & ); + CAutoLockT<MUTEX_TYPE> &operator=( const CAutoLockT<MUTEX_TYPE> & ); + + // No move assignment because no default construction. + CAutoLockT<MUTEX_TYPE> &operator=( CAutoLockT<MUTEX_TYPE> && ); +}; + +typedef CAutoLockT<CThreadMutex> CAutoLock; + +template < typename MUTEX_TYPE > +inline CAutoLockT<MUTEX_TYPE> make_auto_lock( MUTEX_TYPE& lock, const char* pMutexname, const char* pFilename, int nLineNum, int nMinReportDurationUs = 1 ) +{ + return CAutoLockT<MUTEX_TYPE>( lock, pMutexname, pFilename, nLineNum, nMinReportDurationUs ); +} + +//--------------------------------------------------------- + +#define AUTO_LOCK( mutex ) \ + auto UNIQUE_ID = make_auto_lock( mutex, #mutex, __FILE__, __LINE__ ); + +#define AUTO_LOCK_D( mutex, minDurationUs ) \ + auto UNIQUE_ID = make_auto_lock( mutex, #mutex, __FILE__, __LINE__, minDurationUs ); + +#define LOCAL_THREAD_LOCK_( tag ) \ + ; \ + static CThreadFastMutex autoMutex_##tag; \ + AUTO_LOCK( autoMutex_##tag ) + +#define LOCAL_THREAD_LOCK() \ + LOCAL_THREAD_LOCK_(_) + +//----------------------------------------------------------------------------- +// +// Base class for event, semaphore and mutex objects. +// +//----------------------------------------------------------------------------- + +class PLATFORM_CLASS CThreadSyncObject +{ +public: + ~CThreadSyncObject(); + + //----------------------------------------------------- + // Query if object is useful + //----------------------------------------------------- + bool operator!() const; + + //----------------------------------------------------- + // Access handle + //----------------------------------------------------- +#ifdef _WIN32 + operator HANDLE() { return GetHandle(); } + const HANDLE GetHandle() const { return m_hSyncObject; } +#endif + //----------------------------------------------------- + // Wait for a signal from the object + //----------------------------------------------------- + bool Wait( uint32 dwTimeout = TT_INFINITE ); + +protected: + CThreadSyncObject(); + void AssertUseable(); + +#ifdef _WIN32 + HANDLE m_hSyncObject; + bool m_bCreatedHandle; +#elif defined(POSIX) + pthread_mutex_t m_Mutex; + pthread_cond_t m_Condition; + bool m_bInitalized; + int m_cSet; + bool m_bManualReset; + bool m_bWakeForEvent; +#else +#error "Implement me" +#endif + +private: + CThreadSyncObject( const CThreadSyncObject & ); + CThreadSyncObject &operator=( const CThreadSyncObject & ); +}; + + +//----------------------------------------------------------------------------- +// +// Wrapper for unnamed event objects +// +//----------------------------------------------------------------------------- + +#if defined( _WIN32 ) + +//----------------------------------------------------------------------------- +// +// CThreadSemaphore +// +//----------------------------------------------------------------------------- + +class PLATFORM_CLASS CThreadSemaphore : public CThreadSyncObject +{ +public: + CThreadSemaphore(long initialValue, long maxValue); + + //----------------------------------------------------- + // Increases the count of the semaphore object by a specified + // amount. Wait() decreases the count by one on return. + //----------------------------------------------------- + bool Release(long releaseCount = 1, long * pPreviousCount = NULL ); + +private: + CThreadSemaphore(const CThreadSemaphore &); + CThreadSemaphore &operator=(const CThreadSemaphore &); +}; + + +//----------------------------------------------------------------------------- +// +// A mutex suitable for out-of-process, multi-processor usage +// +//----------------------------------------------------------------------------- + +class PLATFORM_CLASS CThreadFullMutex : public CThreadSyncObject +{ +public: + CThreadFullMutex( bool bEstablishInitialOwnership = false, const char * pszName = NULL ); + + //----------------------------------------------------- + // Release ownership of the mutex + //----------------------------------------------------- + bool Release(); + + // To match regular CThreadMutex: + void Lock() { Wait(); } + void Lock( unsigned timeout ) { Wait( timeout ); } + void Unlock() { Release(); } + bool AssertOwnedByCurrentThread() { return true; } + void SetTrace( bool ) {} + +private: + CThreadFullMutex( const CThreadFullMutex & ); + CThreadFullMutex &operator=( const CThreadFullMutex & ); +}; +#endif + + +class PLATFORM_CLASS CThreadEvent : public CThreadSyncObject +{ +public: + CThreadEvent( bool fManualReset = false ); +#ifdef WIN32 + CThreadEvent( HANDLE hHandle ); +#endif + //----------------------------------------------------- + // Set the state to signaled + //----------------------------------------------------- + bool Set(); + + //----------------------------------------------------- + // Set the state to nonsignaled + //----------------------------------------------------- + bool Reset(); + + //----------------------------------------------------- + // Check if the event is signaled + //----------------------------------------------------- + bool Check(); + + bool Wait( uint32 dwTimeout = TT_INFINITE ); + +private: + CThreadEvent( const CThreadEvent & ); + CThreadEvent &operator=( const CThreadEvent & ); +}; + +// Hard-wired manual event for use in array declarations +class CThreadManualEvent : public CThreadEvent +{ +public: + CThreadManualEvent() + : CThreadEvent( true ) + { + } +}; + +inline int ThreadWaitForEvents( int nEvents, CThreadEvent * const *pEvents, bool bWaitAll = true, unsigned timeout = TT_INFINITE ) +{ +#ifdef POSIX + Assert( nEvents == 1); + if ( pEvents[0]->Wait( timeout ) ) + return WAIT_OBJECT_0; + else + return WAIT_TIMEOUT; +#else + HANDLE handles[64]; + for ( int i = 0; i < min( nEvents, (int)ARRAYSIZE(handles) ); i++ ) + handles[i] = pEvents[i]->GetHandle(); + return ThreadWaitForObjects( nEvents, handles, bWaitAll, timeout ); +#endif +} + +//----------------------------------------------------------------------------- +// +// CThreadRWLock +// +//----------------------------------------------------------------------------- + +class PLATFORM_CLASS CThreadRWLock +{ +public: + CThreadRWLock(); + + void LockForRead(); + void UnlockRead(); + void LockForWrite(); + void UnlockWrite(); + + void LockForRead() const { const_cast<CThreadRWLock *>(this)->LockForRead(); } + void UnlockRead() const { const_cast<CThreadRWLock *>(this)->UnlockRead(); } + void LockForWrite() const { const_cast<CThreadRWLock *>(this)->LockForWrite(); } + void UnlockWrite() const { const_cast<CThreadRWLock *>(this)->UnlockWrite(); } + +private: + void WaitForRead(); + +#ifdef WIN32 + CThreadFastMutex m_mutex; +#else + CThreadMutex m_mutex; +#endif + CThreadEvent m_CanWrite; + CThreadEvent m_CanRead; + + int m_nWriters; + int m_nActiveReaders; + int m_nPendingReaders; +}; + +//----------------------------------------------------------------------------- +// +// CThreadSpinRWLock +// +//----------------------------------------------------------------------------- + +class ALIGN8 PLATFORM_CLASS CThreadSpinRWLock +{ +public: + CThreadSpinRWLock() { COMPILE_TIME_ASSERT( sizeof( LockInfo_t ) == sizeof( int64 ) ); Assert( (intp)this % 8 == 0 ); memset( this, 0, sizeof( *this ) ); } + + bool TryLockForWrite(); + bool TryLockForRead(); + + void LockForRead(); + void UnlockRead(); + void LockForWrite(); + void UnlockWrite(); + + bool TryLockForWrite() const { return const_cast<CThreadSpinRWLock *>(this)->TryLockForWrite(); } + bool TryLockForRead() const { return const_cast<CThreadSpinRWLock *>(this)->TryLockForRead(); } + void LockForRead() const { const_cast<CThreadSpinRWLock *>(this)->LockForRead(); } + void UnlockRead() const { const_cast<CThreadSpinRWLock *>(this)->UnlockRead(); } + void LockForWrite() const { const_cast<CThreadSpinRWLock *>(this)->LockForWrite(); } + void UnlockWrite() const { const_cast<CThreadSpinRWLock *>(this)->UnlockWrite(); } + +private: + struct LockInfo_t + { + uint32 m_writerId; + int m_nReaders; + }; + + bool AssignIf( const LockInfo_t &newValue, const LockInfo_t &comperand ); + bool TryLockForWrite( const uint32 threadId ); + void SpinLockForWrite( const uint32 threadId ); + + volatile LockInfo_t m_lockInfo; + CInterlockedInt m_nWriters; +} ALIGN8_POST; + +//----------------------------------------------------------------------------- +// +// A thread wrapper similar to a Java thread. +// +//----------------------------------------------------------------------------- + +class PLATFORM_CLASS CThread +{ +public: + CThread(); + virtual ~CThread(); + + //----------------------------------------------------- + + const char *GetName(); + void SetName( const char * ); + + size_t CalcStackDepth( void *pStackVariable ) { return ((byte *)m_pStackBase - (byte *)pStackVariable); } + + //----------------------------------------------------- + // Functions for the other threads + //----------------------------------------------------- + + // Start thread running - error if already running + virtual bool Start( unsigned nBytesStack = 0 ); + + // Returns true if thread has been created and hasn't yet exited + bool IsAlive(); + + // This method causes the current thread to wait until this thread + // is no longer alive. + bool Join( unsigned timeout = TT_INFINITE ); + +#ifdef _WIN32 + // Access the thread handle directly + HANDLE GetThreadHandle(); + uint GetThreadId(); +#elif defined( LINUX ) + uint GetThreadId(); +#endif + + //----------------------------------------------------- + + int GetResult(); + + //----------------------------------------------------- + // Functions for both this, and maybe, and other threads + //----------------------------------------------------- + + // Forcibly, abnormally, but relatively cleanly stop the thread + void Stop( int exitCode = 0 ); + + // Get the priority + int GetPriority() const; + + // Set the priority + bool SetPriority( int ); + + // Request a thread to suspend, this must ONLY be called from the thread itself, not the main thread + // This suspend variant causes the thread in question to suspend at a known point in its execution + // which means you don't risk the global deadlocks/hangs potentially caused by the raw Suspend() call + void SuspendCooperative(); + + // Resume a previously suspended thread from the Cooperative call + void ResumeCooperative(); + + // wait for a thread to execute its SuspendCooperative call + void BWaitForThreadSuspendCooperative(); + +#ifndef LINUX + // forcefully Suspend a thread + unsigned int Suspend(); + + // forcefully Resume a previously suspended thread + unsigned int Resume(); +#endif + + // Force hard-termination of thread. Used for critical failures. + bool Terminate( int exitCode = 0 ); + + //----------------------------------------------------- + // Global methods + //----------------------------------------------------- + + // Get the Thread object that represents the current thread, if any. + // Can return NULL if the current thread was not created using + // CThread + static CThread *GetCurrentCThread(); + + // Offer a context switch. Under Win32, equivalent to Sleep(0) +#ifdef Yield +#undef Yield +#endif + static void Yield(); + + // This method causes the current thread to yield and not to be + // scheduled for further execution until a certain amount of real + // time has elapsed, more or less. + static void Sleep( unsigned duration ); + +protected: + + // Optional pre-run call, with ability to fail-create. Note Init() + // is forced synchronous with Start() + virtual bool Init(); + + // Thread will run this function on startup, must be supplied by + // derived class, performs the intended action of the thread. + virtual int Run() = 0; + + // Called when the thread is about to exit, by the about-to-exit thread. + virtual void OnExit(); + + // Called after OnExit when a thread finishes or is killed. Not virtual because no inherited classes + // override it and we don't want to change the vtable from the published SDK version. + void Cleanup(); + + bool WaitForCreateComplete( CThreadEvent *pEvent ); + + // "Virtual static" facility + typedef unsigned (__stdcall *ThreadProc_t)( void * ); + virtual ThreadProc_t GetThreadProc(); + virtual bool IsThreadRunning(); + + CThreadMutex m_Lock; + +#ifdef WIN32 + ThreadHandle_t GetThreadID() const { return (ThreadHandle_t)m_hThread; } +#else + ThreadId_t GetThreadID() const { return (ThreadId_t)m_threadId; } +#endif + +private: + enum Flags + { + SUPPORT_STOP_PROTOCOL = 1 << 0 + }; + + // Thread initially runs this. param is actually 'this'. function + // just gets this and calls ThreadProc + struct ThreadInit_t + { + CThread * pThread; + CThreadEvent *pInitCompleteEvent; + bool * pfInitSuccess; + }; + + static unsigned __stdcall ThreadProc( void * pv ); + + // make copy constructor and assignment operator inaccessible + CThread( const CThread & ); + CThread &operator=( const CThread & ); + +#ifdef _WIN32 + HANDLE m_hThread; + ThreadId_t m_threadId; +#elif defined(POSIX) + pthread_t m_threadId; +#endif + CInterlockedInt m_nSuspendCount; + CThreadEvent m_SuspendEvent; + CThreadEvent m_SuspendEventSignal; + int m_result; + char m_szName[32]; + void * m_pStackBase; + unsigned m_flags; +}; + +//----------------------------------------------------------------------------- +// +// A helper class to let you sleep a thread for memory validation, you need to handle +// m_bSleepForValidate in your ::Run() call and set m_bSleepingForValidate when sleeping +// +//----------------------------------------------------------------------------- +class PLATFORM_CLASS CValidatableThread : public CThread +{ +public: + CValidatableThread() + { + m_bSleepForValidate = false; + m_bSleepingForValidate = false; + } + +#ifdef DBGFLAG_VALIDATE + virtual void SleepForValidate() { m_bSleepForValidate = true; } + bool BSleepingForValidate() { return m_bSleepingForValidate; } + virtual void WakeFromValidate() { m_bSleepForValidate = false; } +#endif +protected: + bool m_bSleepForValidate; + bool m_bSleepingForValidate; +}; + +//----------------------------------------------------------------------------- +// Simple thread class encompasses the notion of a worker thread, handing +// synchronized communication. +//----------------------------------------------------------------------------- + + +// These are internal reserved error results from a call attempt +enum WTCallResult_t +{ + WTCR_FAIL = -1, + WTCR_TIMEOUT = -2, + WTCR_THREAD_GONE = -3, +}; + +class CFunctor; +class PLATFORM_CLASS CWorkerThread : public CThread +{ +public: + CWorkerThread(); + + //----------------------------------------------------- + // + // Inter-thread communication + // + // Calls in either direction take place on the same "channel." + // Seperate functions are specified to make identities obvious + // + //----------------------------------------------------- + + // Master: Signal the thread, and block for a response + int CallWorker( unsigned, unsigned timeout = TT_INFINITE, bool fBoostWorkerPriorityToMaster = true, CFunctor *pParamFunctor = NULL ); + + // Worker: Signal the thread, and block for a response + int CallMaster( unsigned, unsigned timeout = TT_INFINITE ); + + // Wait for the next request + bool WaitForCall( unsigned dwTimeout, unsigned *pResult = NULL ); + bool WaitForCall( unsigned *pResult = NULL ); + + // Is there a request? + bool PeekCall( unsigned *pParam = NULL, CFunctor **ppParamFunctor = NULL ); + + // Reply to the request + void Reply( unsigned ); + + // Wait for a reply in the case when CallWorker() with timeout != TT_INFINITE + int WaitForReply( unsigned timeout = TT_INFINITE ); + + // If you want to do WaitForMultipleObjects you'll need to include + // this handle in your wait list or you won't be responsive + CThreadEvent &GetCallHandle(); + // Find out what the request was + unsigned GetCallParam( CFunctor **ppParamFunctor = NULL ) const; + + // Boost the worker thread to the master thread, if worker thread is lesser, return old priority + int BoostPriority(); + +protected: +#ifndef _WIN32 +#define __stdcall +#endif + typedef uint32 (__stdcall *WaitFunc_t)( int nEvents, CThreadEvent * const *pEvents, int bWaitAll, uint32 timeout ); + + int Call( unsigned, unsigned timeout, bool fBoost, WaitFunc_t = NULL, CFunctor *pParamFunctor = NULL ); + int WaitForReply( unsigned timeout, WaitFunc_t ); + +private: + CWorkerThread( const CWorkerThread & ); + CWorkerThread &operator=( const CWorkerThread & ); + + CThreadEvent m_EventSend; + CThreadEvent m_EventComplete; + + unsigned m_Param; + CFunctor *m_pParamFunctor; + int m_ReturnVal; +}; + + +// a unidirectional message queue. A queue of type T. Not especially high speed since each message +// is malloced/freed. Note that if your message class has destructors/constructors, they MUST be +// thread safe! +template<class T> class CMessageQueue +{ + CThreadEvent SignalEvent; // signals presence of data + CThreadMutex QueueAccessMutex; + + // the parts protected by the mutex + struct MsgNode + { + MsgNode *Next; + T Data; + }; + + MsgNode *Head; + MsgNode *Tail; + +public: + CMessageQueue( void ) + { + Head = Tail = NULL; + } + + // check for a message. not 100% reliable - someone could grab the message first + bool MessageWaiting( void ) + { + return ( Head != NULL ); + } + + void WaitMessage( T *pMsg ) + { + for(;;) + { + while( ! MessageWaiting() ) + SignalEvent.Wait(); + QueueAccessMutex.Lock(); + if (! Head ) + { + // multiple readers could make this null + QueueAccessMutex.Unlock(); + continue; + } + *( pMsg ) = Head->Data; + MsgNode *remove_this = Head; + Head = Head->Next; + if (! Head) // if empty, fix tail ptr + Tail = NULL; + QueueAccessMutex.Unlock(); + delete remove_this; + break; + } + } + + void QueueMessage( T const &Msg) + { + MsgNode *new1=new MsgNode; + new1->Data=Msg; + new1->Next=NULL; + QueueAccessMutex.Lock(); + if ( Tail ) + { + Tail->Next=new1; + Tail = new1; + } + else + { + Head = new1; + Tail = new1; + } + SignalEvent.Set(); + QueueAccessMutex.Unlock(); + } +}; + + +//----------------------------------------------------------------------------- +// +// CThreadMutex. Inlining to reduce overhead and to allow client code +// to decide debug status (tracing) +// +//----------------------------------------------------------------------------- + +#ifdef _WIN32 +typedef struct _RTL_CRITICAL_SECTION RTL_CRITICAL_SECTION; +typedef RTL_CRITICAL_SECTION CRITICAL_SECTION; + +#ifndef _X360 +extern "C" +{ + void __declspec(dllimport) __stdcall InitializeCriticalSection(CRITICAL_SECTION *); + void __declspec(dllimport) __stdcall EnterCriticalSection(CRITICAL_SECTION *); + void __declspec(dllimport) __stdcall LeaveCriticalSection(CRITICAL_SECTION *); + void __declspec(dllimport) __stdcall DeleteCriticalSection(CRITICAL_SECTION *); +}; +#endif + +//--------------------------------------------------------- + +inline void CThreadMutex::Lock() +{ +#ifdef THREAD_MUTEX_TRACING_ENABLED + uint thisThreadID = ThreadGetCurrentId(); + if ( m_bTrace && m_currentOwnerID && ( m_currentOwnerID != thisThreadID ) ) + Msg( "Thread %u about to wait for lock %p owned by %u\n", ThreadGetCurrentId(), (CRITICAL_SECTION *)&m_CriticalSection, m_currentOwnerID ); + #endif + + VCRHook_EnterCriticalSection((CRITICAL_SECTION *)&m_CriticalSection); + + #ifdef THREAD_MUTEX_TRACING_ENABLED + if (m_lockCount == 0) + { + // we now own it for the first time. Set owner information + m_currentOwnerID = thisThreadID; + if ( m_bTrace ) + Msg( "Thread %u now owns lock %p\n", m_currentOwnerID, (CRITICAL_SECTION *)&m_CriticalSection ); + } + m_lockCount++; + #endif +} + +//--------------------------------------------------------- + +inline void CThreadMutex::Unlock() +{ + #ifdef THREAD_MUTEX_TRACING_ENABLED + AssertMsg( m_lockCount >= 1, "Invalid unlock of thread lock" ); + m_lockCount--; + if (m_lockCount == 0) + { + if ( m_bTrace ) + Msg( "Thread %u releasing lock %p\n", m_currentOwnerID, (CRITICAL_SECTION *)&m_CriticalSection ); + m_currentOwnerID = 0; + } + #endif + LeaveCriticalSection((CRITICAL_SECTION *)&m_CriticalSection); +} + +//--------------------------------------------------------- + +inline bool CThreadMutex::AssertOwnedByCurrentThread() +{ +#ifdef THREAD_MUTEX_TRACING_ENABLED + if (ThreadGetCurrentId() == m_currentOwnerID) + return true; + AssertMsg3( 0, "Expected thread %u as owner of lock %p, but %u owns", ThreadGetCurrentId(), (CRITICAL_SECTION *)&m_CriticalSection, m_currentOwnerID ); + return false; +#else + return true; +#endif +} + +//--------------------------------------------------------- + +inline void CThreadMutex::SetTrace( bool bTrace ) +{ +#ifdef THREAD_MUTEX_TRACING_ENABLED + m_bTrace = bTrace; +#endif +} + +//--------------------------------------------------------- + +#elif defined(POSIX) + +inline CThreadMutex::CThreadMutex() +{ + // enable recursive locks as we need them + pthread_mutexattr_init( &m_Attr ); + pthread_mutexattr_settype( &m_Attr, PTHREAD_MUTEX_RECURSIVE ); + pthread_mutex_init( &m_Mutex, &m_Attr ); +} + +//--------------------------------------------------------- + +inline CThreadMutex::~CThreadMutex() +{ + pthread_mutex_destroy( &m_Mutex ); +} + +//--------------------------------------------------------- + +inline void CThreadMutex::Lock() +{ + pthread_mutex_lock( &m_Mutex ); +} + +//--------------------------------------------------------- + +inline void CThreadMutex::Unlock() +{ + pthread_mutex_unlock( &m_Mutex ); +} + +//--------------------------------------------------------- + +inline bool CThreadMutex::AssertOwnedByCurrentThread() +{ + return true; +} + +//--------------------------------------------------------- + +inline void CThreadMutex::SetTrace(bool fTrace) +{ +} + +#endif // POSIX + +//----------------------------------------------------------------------------- +// +// CThreadRWLock inline functions +// +//----------------------------------------------------------------------------- + +inline CThreadRWLock::CThreadRWLock() +: m_CanRead( true ), + m_nWriters( 0 ), + m_nActiveReaders( 0 ), + m_nPendingReaders( 0 ) +{ +} + +inline void CThreadRWLock::LockForRead() +{ + m_mutex.Lock(); + if ( m_nWriters) + { + WaitForRead(); + } + m_nActiveReaders++; + m_mutex.Unlock(); +} + +inline void CThreadRWLock::UnlockRead() +{ + m_mutex.Lock(); + m_nActiveReaders--; + if ( m_nActiveReaders == 0 && m_nWriters != 0 ) + { + m_CanWrite.Set(); + } + m_mutex.Unlock(); +} + + +//----------------------------------------------------------------------------- +// +// CThreadSpinRWLock inline functions +// +//----------------------------------------------------------------------------- + +inline bool CThreadSpinRWLock::AssignIf( const LockInfo_t &newValue, const LockInfo_t &comperand ) +{ + return ThreadInterlockedAssignIf64( (int64 *)&m_lockInfo, *((int64 *)&newValue), *((int64 *)&comperand) ); +} + +inline bool CThreadSpinRWLock::TryLockForWrite( const uint32 threadId ) +{ + // In order to grab a write lock, there can be no readers and no owners of the write lock + if ( m_lockInfo.m_nReaders > 0 || ( m_lockInfo.m_writerId && m_lockInfo.m_writerId != threadId ) ) + { + return false; + } + + static const LockInfo_t oldValue = { 0, 0 }; + LockInfo_t newValue = { threadId, 0 }; + const bool bSuccess = AssignIf( newValue, oldValue ); +#if defined(_X360) + if ( bSuccess ) + { + // X360TBD: Serious perf implications. Not Yet. __sync(); + } +#endif + return bSuccess; +} + +inline bool CThreadSpinRWLock::TryLockForWrite() +{ + m_nWriters++; + if ( !TryLockForWrite( ThreadGetCurrentId() ) ) + { + m_nWriters--; + return false; + } + return true; +} + +inline bool CThreadSpinRWLock::TryLockForRead() +{ + if ( m_nWriters != 0 ) + { + return false; + } + // In order to grab a write lock, the number of readers must not change and no thread can own the write + LockInfo_t oldValue; + LockInfo_t newValue; + + oldValue.m_nReaders = m_lockInfo.m_nReaders; + oldValue.m_writerId = 0; + newValue.m_nReaders = oldValue.m_nReaders + 1; + newValue.m_writerId = 0; + + const bool bSuccess = AssignIf( newValue, oldValue ); +#if defined(_X360) + if ( bSuccess ) + { + // X360TBD: Serious perf implications. Not Yet. __sync(); + } +#endif + return bSuccess; +} + +inline void CThreadSpinRWLock::LockForWrite() +{ + const uint32 threadId = ThreadGetCurrentId(); + + m_nWriters++; + + if ( !TryLockForWrite( threadId ) ) + { + ThreadPause(); + SpinLockForWrite( threadId ); + } +} + +// read data from a memory address +template<class T> FORCEINLINE T ReadVolatileMemory( T const *pPtr ) +{ + volatile const T * pVolatilePtr = ( volatile const T * ) pPtr; + return *pVolatilePtr; +} + +//----------------------------------------------------------------------------- + +#if defined( _WIN32 ) +#pragma warning(pop) +#endif + +#endif // THREADTOOLS_H diff --git a/public/tier0/tmapi_dummy.h b/public/tier0/tmapi_dummy.h new file mode 100644 index 0000000..8a6aebc --- /dev/null +++ b/public/tier0/tmapi_dummy.h @@ -0,0 +1,84 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// This is the null header file used to remove Telemetry calls. + +#define TMERR_DISABLED 1 +#define TMPRINTF_TOKEN_NONE 0 + +#define tmGetSessionName(...) +#define tmEndTryLock(...) +#define tmEndTryLockEx(...) +#define tmSetLockState(...) +#define tmSetLockStateEx(...) +#define tmSetLockStateMinTime(...) 0 +#define tmSetLockStateMinTimeEx(...) 0 +#define tmSignalLockCount(...) + +#define tmCheckVersion(...) 0 +#define tmGetCallStack(...) 0 +#define tmSendCallStack( ... ) TMPRINTF_TOKEN_NONE +#define tmGetCallStackR(...) 0 +#define tmSendCallStackR(...) TMPRINTF_TOKEN_NONE +#define tmSendCallStackWithSkipR(...) TMPRINTF_TOKEN_NONE + +#define tmGetVersion(...) 0 +#define tmStartup(...) TMERR_DISABLED +#define tmGetPlatformInformation(...) TMERR_DISABLED +#define tmInitializeContext(...) TMERR_DISABLED +#define tmShutdown(...) TMERR_DISABLED + +#define tmEnter(...) +#define tmEnterEx(...) +#define tmZone(...) +#define tmZoneFiltered(...) +#define tmLeave(...) +#define tmLeaveEx(...) + +#define tmBeginTimeSpan(...) +#define tmEndTimeSpan(...) + +#define tmBeginTimeSpanAt(...) +#define tmEndTimeSpanAt(...) + +#define tmDynamicString(...) "" + +#define tmEmitAccumulationZone(...) + +#define tmGetStati(...) 0 + +#define tmSetVariable(...) + +#define tmBlob(...) +#define tmDisjointBlob(...) +#define tmSetTimelineSectionName(...) +#define tmThreadName(...) +#define tmLockName(...) +#define tmMessage(...) +#define tmAlloc(...) +#define tmAllocEx(...) + +#define tmTryLock(...) +#define tmTryLockEx(...) + +#define tmPlot(...) +#define tmPlotF32(...) +#define tmPlotF64(...) +#define tmPlotI32(...) +#define tmPlotU32(...) +#define tmPlotS32(...) +#define tmPlotI64(...) +#define tmPlotU64(...) +#define tmPlotS64(...) + +#define tmPPUGetListener(...) TMERR_DISABLED +#define tmPPURegisterSPUProgram(...) TMERR_DISABLED +#define tmSPUBindContextToListener(...) +#define tmSPUUpdateTime(...) +#define tmSPUFlushImage(...) + +#define NTELEMETRY 1 + +#define TM_CONTEXT_LITE(val) ((char*)(val)) +#define TM_CONTEXT_FULL(val) ((char*)(val)) + +typedef char *HTELEMETRY; + diff --git a/public/tier0/tslist.h b/public/tier0/tslist.h new file mode 100644 index 0000000..d6610e0 --- /dev/null +++ b/public/tier0/tslist.h @@ -0,0 +1,1004 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// LIFO from disassembly of Windows API and http://perso.wanadoo.fr/gmem/evenements/jim2002/articles/L17_Fober.pdf +// FIFO from http://perso.wanadoo.fr/gmem/evenements/jim2002/articles/L17_Fober.pdf +// +//============================================================================= + +#ifndef TSLIST_H +#define TSLIST_H + +#if defined( _WIN32 ) +#pragma once +// Suppress this spurious warning: +// warning C4700: uninitialized local variable 'oldHead' used +#pragma warning( push ) +#pragma warning( disable : 4700 ) +#endif + +#if defined( USE_NATIVE_SLIST ) && !defined( _X360 ) +#define WIN32_LEAN_AND_MEAN +#include <windows.h> +#endif + +#include "tier0/dbg.h" +#include "tier0/threadtools.h" +#include "tier0/memalloc.h" +#include "tier0/memdbgoff.h" + +#if defined( _X360 ) +#define USE_NATIVE_SLIST +#endif + +//----------------------------------------------------------------------------- + +#if defined( PLATFORM_64BITS ) + +#define TSLIST_HEAD_ALIGNMENT 16 +#define TSLIST_NODE_ALIGNMENT 16 +inline bool ThreadInterlockedAssignIf64x128( volatile int128 *pDest, const int128 &value, const int128 &comperand ) + { return ThreadInterlockedAssignIf128( pDest, value, comperand ); } +#else +#define TSLIST_HEAD_ALIGNMENT 8 +#define TSLIST_NODE_ALIGNMENT 8 +inline bool ThreadInterlockedAssignIf64x128( volatile int64 *pDest, const int64 value, const int64 comperand ) + { return ThreadInterlockedAssignIf64( pDest, value, comperand ); } +#endif + +#ifdef _MSC_VER +#define TSLIST_HEAD_ALIGN DECL_ALIGN(TSLIST_HEAD_ALIGNMENT) +#define TSLIST_NODE_ALIGN DECL_ALIGN(TSLIST_NODE_ALIGNMENT) +#define TSLIST_HEAD_ALIGN_POST +#define TSLIST_NODE_ALIGN_POST +#elif defined( GNUC ) +#define TSLIST_HEAD_ALIGN +#define TSLIST_NODE_ALIGN +#define TSLIST_HEAD_ALIGN_POST DECL_ALIGN(TSLIST_HEAD_ALIGNMENT) +#define TSLIST_NODE_ALIGN_POST DECL_ALIGN(TSLIST_NODE_ALIGNMENT) +#elif defined( _PS3 ) +#define TSLIST_HEAD_ALIGNMENT 8 +#define TSLIST_NODE_ALIGNMENT 8 + +#define TSLIST_HEAD_ALIGN ALIGN8 +#define TSLIST_NODE_ALIGN ALIGN8 +#define TSLIST_HEAD_ALIGN_POST ALIGN8_POST +#define TSLIST_NODE_ALIGN_POST ALIGN8_POST + +#else +#error +#endif + +//----------------------------------------------------------------------------- + +PLATFORM_INTERFACE bool RunTSQueueTests( int nListSize = 10000, int nTests = 1 ); +PLATFORM_INTERFACE bool RunTSListTests( int nListSize = 10000, int nTests = 1 ); + +//----------------------------------------------------------------------------- +// Lock free list. +//----------------------------------------------------------------------------- +//#define USE_NATIVE_SLIST + +#ifdef USE_NATIVE_SLIST +typedef SLIST_ENTRY TSLNodeBase_t; +typedef SLIST_HEADER TSLHead_t; +#else +struct TSLIST_NODE_ALIGN TSLNodeBase_t +{ + TSLNodeBase_t *Next; // name to match Windows +} TSLIST_NODE_ALIGN_POST; + +union TSLIST_HEAD_ALIGN TSLHead_t +{ + struct Value_t + { + TSLNodeBase_t *Next; + // <sergiy> Depth must be in the least significant halfword when atomically loading into register, + // to avoid carrying digits from Sequence. Carrying digits from Depth to Sequence is ok, + // because Sequence can be pretty much random. We could operate on both of them separately, + // but it could perhaps (?) lead to problems with store forwarding. I don't know 'cause I didn't + // performance-test or design original code, I'm just making it work on PowerPC. + #ifdef VALVE_BIG_ENDIAN + int16 Sequence; + int16 Depth; + #else + int16 Depth; + int16 Sequence; + #endif +#ifdef PLATFORM_64BITS + int32 Padding; +#endif + } value; + + struct Value32_t + { + TSLNodeBase_t *Next_do_not_use_me; + int32 DepthAndSequence; + } value32; + +#ifdef PLATFORM_64BITS + int128 value64x128; +#else + int64 value64x128; +#endif +} TSLIST_HEAD_ALIGN_POST; + +#endif + +//------------------------------------- +class CTSListBase +{ +public: + + // override new/delete so we can guarantee 8-byte aligned allocs + static void * operator new( size_t size ) + { + CTSListBase *pNode = (CTSListBase *)MemAlloc_AllocAligned( size, TSLIST_HEAD_ALIGNMENT, __FILE__, __LINE__ ); + return pNode; + } + + static void * operator new( size_t size, int nBlockUse, const char *pFileName, int nLine ) + { + CTSListBase *pNode = (CTSListBase *)MemAlloc_AllocAligned( size, TSLIST_HEAD_ALIGNMENT, pFileName, nLine ); + return pNode; + } + + static void operator delete( void *p) + { + MemAlloc_FreeAligned( p ); + } + + static void operator delete( void *p, int nBlockUse, const char *pFileName, int nLine ) + { + MemAlloc_FreeAligned( p ); + } + +private: + // These ain't gonna work + static void * operator new[] ( size_t size ); + static void operator delete [] ( void *p); + +public: + + CTSListBase() + { + if ( ((size_t)&m_Head) % TSLIST_HEAD_ALIGNMENT != 0 ) + { + Error( "CTSListBase: Misaligned list\n" ); + DebuggerBreak(); + } + +#ifdef USE_NATIVE_SLIST + InitializeSListHead( &m_Head ); +#elif defined(PLATFORM_64BITS) + m_Head.value64x128 = int128_zero(); +#else + m_Head.value64x128 = (int64)0; +#endif + } + + ~CTSListBase() + { + Detach(); + } + + TSLNodeBase_t *Push( TSLNodeBase_t *pNode ) + { +#ifdef _DEBUG + if ( (size_t)pNode % TSLIST_NODE_ALIGNMENT != 0 ) + { + Error( "CTSListBase: Misaligned node\n" ); + DebuggerBreak(); + } +#endif + +#ifdef USE_NATIVE_SLIST +#ifdef _X360 + // integrated write-release barrier + return (TSLNodeBase_t *)InterlockedPushEntrySListRelease( &m_Head, pNode ); +#else + return (TSLNodeBase_t *)InterlockedPushEntrySList( &m_Head, pNode ); +#endif +#else + TSLHead_t oldHead; + TSLHead_t newHead; + + #if defined( PLATFORM_PS3 ) || defined( PLATFORM_X360 ) + __lwsync(); // write-release barrier + #endif + +#ifdef PLATFORM_64BITS + newHead.value.Padding = 0; +#endif + for (;;) + { + oldHead.value64x128 = m_Head.value64x128; + pNode->Next = oldHead.value.Next; + newHead.value.Next = pNode; + + newHead.value32.DepthAndSequence = oldHead.value32.DepthAndSequence + 0x10001; + + + if ( ThreadInterlockedAssignIf64x128( &m_Head.value64x128, newHead.value64x128, oldHead.value64x128 ) ) + { + break; + } + ThreadPause(); + }; + + return (TSLNodeBase_t *)oldHead.value.Next; +#endif + } + + TSLNodeBase_t *Pop() + { +#ifdef USE_NATIVE_SLIST +#ifdef _X360 + // integrated read-acquire barrier + TSLNodeBase_t *pNode = (TSLNodeBase_t *)InterlockedPopEntrySListAcquire( &m_Head ); +#else + TSLNodeBase_t *pNode = (TSLNodeBase_t *)InterlockedPopEntrySList( &m_Head ); +#endif + return pNode; +#else + TSLHead_t oldHead; + TSLHead_t newHead; + +#ifdef PLATFORM_64BITS + newHead.value.Padding = 0; +#endif + for (;;) + { + oldHead.value64x128 = m_Head.value64x128; + if ( !oldHead.value.Next ) + return NULL; + + newHead.value.Next = oldHead.value.Next->Next; + newHead.value32.DepthAndSequence = oldHead.value32.DepthAndSequence - 1; + + + if ( ThreadInterlockedAssignIf64x128( &m_Head.value64x128, newHead.value64x128, oldHead.value64x128 ) ) + { + #if defined( PLATFORM_PS3 ) || defined( PLATFORM_X360 ) + __lwsync(); // read-acquire barrier + #endif + break; + } + ThreadPause(); + }; + + return (TSLNodeBase_t *)oldHead.value.Next; +#endif + } + + TSLNodeBase_t *Detach() + { +#ifdef USE_NATIVE_SLIST + TSLNodeBase_t *pBase = (TSLNodeBase_t *)InterlockedFlushSList( &m_Head ); +#if defined( _X360 ) || defined( _PS3 ) + __lwsync(); // read-acquire barrier +#endif + return pBase; +#else + TSLHead_t oldHead; + TSLHead_t newHead; + +#ifdef PLATFORM_64BITS + newHead.value.Padding = 0; +#endif + do + { + ThreadPause(); + + oldHead.value64x128 = m_Head.value64x128; + if ( !oldHead.value.Next ) + return NULL; + + newHead.value.Next = NULL; + // <sergiy> the reason for AND'ing it instead of poking a short into memory + // is probably to avoid store forward issues, but I'm not sure because + // I didn't construct this code. In any case, leaving it as is on big-endian + newHead.value32.DepthAndSequence = oldHead.value32.DepthAndSequence & 0xffff0000; + + } while( !ThreadInterlockedAssignIf64x128( &m_Head.value64x128, newHead.value64x128, oldHead.value64x128 ) ); + + return (TSLNodeBase_t *)oldHead.value.Next; +#endif + } + + TSLHead_t *AccessUnprotected() + { + return &m_Head; + } + + int Count() const + { +#ifdef USE_NATIVE_SLIST + return QueryDepthSList( const_cast<TSLHead_t*>( &m_Head ) ); +#else + return m_Head.value.Depth; +#endif + } + +private: + TSLHead_t m_Head; +} TSLIST_HEAD_ALIGN_POST; + +//------------------------------------- + +template <typename T> +class TSLIST_HEAD_ALIGN CTSSimpleList : public CTSListBase +{ +public: + void Push( T *pNode ) + { + Assert( sizeof(T) >= sizeof(TSLNodeBase_t) ); + CTSListBase::Push( (TSLNodeBase_t *)pNode ); + } + + T *Pop() + { + return (T *)CTSListBase::Pop(); + } +} TSLIST_HEAD_ALIGN_POST; + +//------------------------------------- +// this is a replacement for CTSList<> and CObjectPool<> that does not +// have a per-item, per-alloc new/delete overhead +// similar to CTSSimpleList except that it allocates it's own pool objects +// and frees them on destruct. Also it does not overlay the TSNodeBase_t memory +// on T's memory +template< class T > +class TSLIST_HEAD_ALIGN CTSPool : public CTSListBase +{ + // packs the node and the item (T) into a single struct and pools those + struct TSLIST_NODE_ALIGN simpleTSPoolStruct_t : public TSLNodeBase_t + { + T elem; + } TSLIST_NODE_ALIGN_POST; + +public: + + ~CTSPool() + { + Purge(); + } + + void Purge() + { + simpleTSPoolStruct_t *pNode = NULL; + while ( 1 ) + { + pNode = (simpleTSPoolStruct_t *)CTSListBase::Pop(); + if ( !pNode ) + break; + delete pNode; + } + } + + void PutObject( T *pInfo ) + { + char *pElem = (char *)pInfo; + pElem -= offsetof(simpleTSPoolStruct_t,elem); + simpleTSPoolStruct_t *pNode = (simpleTSPoolStruct_t *)pElem; + + CTSListBase::Push( pNode ); + } + + T *GetObject() + { + simpleTSPoolStruct_t *pNode = (simpleTSPoolStruct_t *)CTSListBase::Pop(); + if ( !pNode ) + { + pNode = new simpleTSPoolStruct_t; + } + return &pNode->elem; + } + + // omg windows sdk - why do you #define GetObject()? + FORCEINLINE T *Get() + { + return GetObject(); + } +} TSLIST_HEAD_ALIGN_POST; +//------------------------------------- + +template <typename T> +class TSLIST_HEAD_ALIGN CTSList : public CTSListBase +{ +public: + struct TSLIST_NODE_ALIGN Node_t : public TSLNodeBase_t + { + Node_t() {} + Node_t( const T &init ) : elem( init ) {} + T elem; + + // override new/delete so we can guarantee 8-byte aligned allocs + static void * operator new( size_t size ) + { + Node_t *pNode = (Node_t *)MemAlloc_AllocAligned( size, TSLIST_NODE_ALIGNMENT, __FILE__, __LINE__ ); + return pNode; + } + + // override new/delete so we can guarantee 8-byte aligned allocs + static void * operator new( size_t size, int nBlockUse, const char *pFileName, int nLine ) + { + Node_t *pNode = (Node_t *)MemAlloc_AllocAligned( size, TSLIST_NODE_ALIGNMENT, pFileName, nLine ); + return pNode; + } + + static void operator delete( void *p) + { + MemAlloc_FreeAligned( p ); + } + static void operator delete( void *p, int nBlockUse, const char *pFileName, int nLine ) + { + MemAlloc_FreeAligned( p ); + } + + } TSLIST_NODE_ALIGN_POST; + + ~CTSList() + { + Purge(); + } + + void Purge() + { + Node_t *pCurrent = Detach(); + Node_t *pNext; + while ( pCurrent ) + { + pNext = (Node_t *)pCurrent->Next; + delete pCurrent; + pCurrent = pNext; + } + } + + void RemoveAll() + { + Purge(); + } + + Node_t *Push( Node_t *pNode ) + { + return (Node_t *)CTSListBase::Push( pNode ); + } + + Node_t *Pop() + { + return (Node_t *)CTSListBase::Pop(); + } + + void PushItem( const T &init ) + { + Push( new Node_t( init ) ); + } + + bool PopItem( T *pResult) + { + Node_t *pNode = Pop(); + if ( !pNode ) + return false; + *pResult = pNode->elem; + delete pNode; + return true; + } + + Node_t *Detach() + { + return (Node_t *)CTSListBase::Detach(); + } + +} TSLIST_HEAD_ALIGN_POST; + +//------------------------------------- + +template <typename T> +class TSLIST_HEAD_ALIGN CTSListWithFreeList : public CTSListBase +{ +public: + struct TSLIST_NODE_ALIGN Node_t : public TSLNodeBase_t + { + Node_t() {} + Node_t( const T &init ) : elem( init ) {} + + T elem; + } TSLIST_NODE_ALIGN_POST; + + ~CTSListWithFreeList() + { + Purge(); + } + + void Purge() + { + Node_t *pCurrent = Detach(); + Node_t *pNext; + while ( pCurrent ) + { + pNext = (Node_t *)pCurrent->Next; + delete pCurrent; + pCurrent = pNext; + } + pCurrent = (Node_t *)m_FreeList.Detach(); + while ( pCurrent ) + { + pNext = (Node_t *)pCurrent->Next; + delete pCurrent; + pCurrent = pNext; + } + } + + void RemoveAll() + { + Node_t *pCurrent = Detach(); + Node_t *pNext; + while ( pCurrent ) + { + pNext = (Node_t *)pCurrent->Next; + m_FreeList.Push( pCurrent ); + pCurrent = pNext; + } + } + + Node_t *Push( Node_t *pNode ) + { + return (Node_t *)CTSListBase::Push( pNode ); + } + + Node_t *Pop() + { + return (Node_t *)CTSListBase::Pop(); + } + + void PushItem( const T &init ) + { + Node_t *pNode = (Node_t *)m_FreeList.Pop(); + if ( !pNode ) + { + pNode = new Node_t; + } + pNode->elem = init; + Push( pNode ); + } + + bool PopItem( T *pResult) + { + Node_t *pNode = Pop(); + if ( !pNode ) + return false; + *pResult = pNode->elem; + m_FreeList.Push( pNode ); + return true; + } + + Node_t *Detach() + { + return (Node_t *)CTSListBase::Detach(); + } + + void FreeNode( Node_t *pNode ) + { + m_FreeList.Push( pNode ); + } + +private: + CTSListBase m_FreeList; +} TSLIST_HEAD_ALIGN_POST; + +//----------------------------------------------------------------------------- +// Lock free queue +// +// A special consideration: the element type should be simple. This code +// actually dereferences freed nodes as part of pop, but later detects +// that. If the item in the queue is a complex type, only bad things can +// come of that. Also, therefore, if you're using Push/Pop instead of +// push item, be aware that the node memory cannot be freed until +// all threads that might have been popping have completed the pop. +// The PushItem()/PopItem() for handles this by keeping a persistent +// free list. Dont mix Push/PushItem. Note also nodes will be freed at the end, +// and are expected to have been allocated with operator new. +//----------------------------------------------------------------------------- + +template <typename T, bool bTestOptimizer = false> +class TSLIST_HEAD_ALIGN CTSQueue +{ +public: + + // override new/delete so we can guarantee 8-byte aligned allocs + static void * operator new( size_t size ) + { + CTSQueue *pNode = (CTSQueue *)MemAlloc_AllocAligned( size, TSLIST_HEAD_ALIGNMENT, __FILE__, __LINE__ ); + return pNode; + } + + // override new/delete so we can guarantee 8-byte aligned allocs + static void * operator new( size_t size, int nBlockUse, const char *pFileName, int nLine ) + { + CTSQueue *pNode = (CTSQueue *)MemAlloc_AllocAligned( size, TSLIST_HEAD_ALIGNMENT, pFileName, nLine ); + return pNode; + } + + static void operator delete( void *p) + { + MemAlloc_FreeAligned( p ); + } + + static void operator delete( void *p, int nBlockUse, const char *pFileName, int nLine ) + { + MemAlloc_FreeAligned( p ); + } + +private: + // These ain't gonna work + static void * operator new[] ( size_t size ) throw() + { + return NULL; + } + + static void operator delete [] ( void *p) + { + } + +public: + + struct TSLIST_NODE_ALIGN Node_t + { + // override new/delete so we can guarantee 8-byte aligned allocs + static void * operator new( size_t size ) + { + Node_t *pNode = (Node_t *)MemAlloc_AllocAligned( size, TSLIST_HEAD_ALIGNMENT, __FILE__, __LINE__ ); + return pNode; + } + + static void * operator new( size_t size, int nBlockUse, const char *pFileName, int nLine ) + { + Node_t *pNode = (Node_t *)MemAlloc_AllocAligned( size, TSLIST_HEAD_ALIGNMENT, pFileName, nLine ); + return pNode; + } + + static void operator delete( void *p) + { + MemAlloc_FreeAligned( p ); + } + + static void operator delete( void *p, int nBlockUse, const char *pFileName, int nLine ) + { + MemAlloc_FreeAligned( p ); + } + + Node_t() {} + Node_t( const T &init ) : elem( init ) {} + + Node_t *pNext; + T elem; + } TSLIST_NODE_ALIGN_POST; + + union TSLIST_HEAD_ALIGN NodeLink_t + { + // override new/delete so we can guarantee 8-byte aligned allocs + static void * operator new( size_t size ) + { + NodeLink_t *pNode = (NodeLink_t *)MemAlloc_AllocAligned( size, TSLIST_HEAD_ALIGNMENT, __FILE__, __LINE__ ); + return pNode; + } + + static void operator delete( void *p) + { + MemAlloc_FreeAligned( p ); + } + + struct Value_t + { + Node_t *pNode; + intp sequence; + } value; + +#ifdef PLATFORM_64BITS + int128 value64x128; +#else + int64 value64x128; +#endif + } TSLIST_HEAD_ALIGN_POST; + + CTSQueue() + { + COMPILE_TIME_ASSERT( sizeof(Node_t) >= sizeof(TSLNodeBase_t) ); + if ( ((size_t)&m_Head) % TSLIST_HEAD_ALIGNMENT != 0 ) + { + Error( "CTSQueue: Misaligned queue\n" ); + DebuggerBreak(); + } + if ( ((size_t)&m_Tail) % TSLIST_HEAD_ALIGNMENT != 0 ) + { + Error( "CTSQueue: Misaligned queue\n" ); + DebuggerBreak(); + } + m_Count = 0; + m_Head.value.sequence = m_Tail.value.sequence = 0; + m_Head.value.pNode = m_Tail.value.pNode = new Node_t; // list always contains a dummy node + m_Head.value.pNode->pNext = End(); + } + + ~CTSQueue() + { + Purge(); + Assert( m_Count == 0 ); + Assert( m_Head.value.pNode == m_Tail.value.pNode ); + Assert( m_Head.value.pNode->pNext == End() ); + delete m_Head.value.pNode; + } + + // Note: Purge, RemoveAll, and Validate are *not* threadsafe + void Purge() + { + if ( IsDebug() ) + { + ValidateQueue(); + } + + Node_t *pNode; + while ( ( pNode = Pop() ) != NULL ) + { + delete pNode; + } + + while ( ( pNode = (Node_t *)m_FreeNodes.Pop() ) != NULL ) + { + delete pNode; + } + + Assert( m_Count == 0 ); + Assert( m_Head.value.pNode == m_Tail.value.pNode ); + Assert( m_Head.value.pNode->pNext == End() ); + + m_Head.value.sequence = m_Tail.value.sequence = 0; + } + + void RemoveAll() + { + if ( IsDebug() ) + { + ValidateQueue(); + } + + Node_t *pNode; + while ( ( pNode = Pop() ) != NULL ) + { + m_FreeNodes.Push( (TSLNodeBase_t *)pNode ); + } + } + + bool ValidateQueue() + { + if ( IsDebug() ) + { + bool bResult = true; + int nNodes = 0; + if ( m_Tail.value.pNode->pNext != End() ) + { + DebuggerBreakIfDebugging(); + bResult = false; + } + + if ( m_Count == 0 ) + { + if ( m_Head.value.pNode != m_Tail.value.pNode ) + { + DebuggerBreakIfDebugging(); + bResult = false; + } + } + + Node_t *pNode = m_Head.value.pNode; + while ( pNode != End() ) + { + nNodes++; + pNode = pNode->pNext; + } + + nNodes--;// skip dummy node + + if ( nNodes != m_Count ) + { + DebuggerBreakIfDebugging(); + bResult = false; + } + + if ( !bResult ) + { + Msg( "Corrupt CTSQueueDetected" ); + } + + return bResult; + } + else + { + return true; + } + } + + void FinishPush( Node_t *pNode, const NodeLink_t &oldTail ) + { + NodeLink_t newTail; + + newTail.value.pNode = pNode; + newTail.value.sequence = oldTail.value.sequence + 1; + + ThreadMemoryBarrier(); + + InterlockedCompareExchangeNodeLink( &m_Tail, newTail, oldTail ); + } + + Node_t *Push( Node_t *pNode ) + { +#ifdef _DEBUG + if ( (size_t)pNode % TSLIST_NODE_ALIGNMENT != 0 ) + { + Error( "CTSListBase: Misaligned node\n" ); + DebuggerBreak(); + } +#endif + + NodeLink_t oldTail; + + pNode->pNext = End(); + + for (;;) + { + oldTail.value.sequence = m_Tail.value.sequence; + oldTail.value.pNode = m_Tail.value.pNode; + if ( InterlockedCompareExchangeNode( &(oldTail.value.pNode->pNext), pNode, End() ) == End() ) + { + break; + } + else + { + // Another thread is trying to push, help it along + FinishPush( oldTail.value.pNode->pNext, oldTail ); + } + } + + FinishPush( pNode, oldTail ); // This can fail if another thread pushed between the sequence and node grabs above. Later pushes or pops corrects + + m_Count++; + + return oldTail.value.pNode; + } + + Node_t *Pop() + { + #define TSQUEUE_BAD_NODE_LINK ( (Node_t *)INT_TO_POINTER( 0xdeadbeef ) ) + NodeLink_t * volatile pHead = &m_Head; + NodeLink_t * volatile pTail = &m_Tail; + Node_t * volatile * pHeadNode = &m_Head.value.pNode; + volatile intp * volatile pHeadSequence = &m_Head.value.sequence; + Node_t * volatile * pTailNode = &pTail->value.pNode; + + NodeLink_t head; + NodeLink_t newHead; + Node_t *pNext; + intp tailSequence; + T elem; + + for (;;) + { + head.value.sequence = *pHeadSequence; // must grab sequence first, which allows condition below to ensure pNext is valid + ThreadMemoryBarrier(); // need a barrier to prevent reordering of these assignments + head.value.pNode = *pHeadNode; + tailSequence = pTail->value.sequence; + pNext = head.value.pNode->pNext; + + // Checking pNext only to force optimizer to not reorder the assignment + // to pNext and the compare of the sequence + if ( !pNext || head.value.sequence != *pHeadSequence ) + continue; + + if ( bTestOptimizer ) + { + if ( pNext == TSQUEUE_BAD_NODE_LINK ) + { + Msg( "Bad node link detected\n" ); + continue; + } + } + + if ( head.value.pNode == *pTailNode ) + { + if ( pNext == End() ) + return NULL; + + // Another thread is trying to push, help it along + NodeLink_t &oldTail = head; // just reuse local memory for head to build old tail + oldTail.value.sequence = tailSequence; // reuse head pNode + FinishPush( pNext, oldTail ); + continue; + } + + if ( pNext != End() ) + { + elem = pNext->elem; // NOTE: next could be a freed node here, by design + newHead.value.pNode = pNext; + newHead.value.sequence = head.value.sequence + 1; + if ( InterlockedCompareExchangeNodeLink( pHead, newHead, head ) ) + { + ThreadMemoryBarrier(); + if ( bTestOptimizer ) + { + head.value.pNode->pNext = TSQUEUE_BAD_NODE_LINK; + } + break; + } + } + } + + m_Count--; + head.value.pNode->elem = elem; + return head.value.pNode; + } + + void FreeNode( Node_t *pNode ) + { + m_FreeNodes.Push( (TSLNodeBase_t *)pNode ); + } + + void PushItem( const T &init ) + { + Node_t *pNode = (Node_t *)m_FreeNodes.Pop(); + if ( pNode ) + { + pNode->elem = init; + } + else + { + pNode = new Node_t( init ); + } + Push( pNode ); + } + + bool PopItem( T *pResult ) + { + Node_t *pNode = Pop(); + if ( !pNode ) + return false; + + *pResult = pNode->elem; + m_FreeNodes.Push( (TSLNodeBase_t *)pNode ); + return true; + } + + int Count() const + { + return m_Count; + } + +private: + Node_t *End() { return (Node_t *)this; } // just need a unique signifier + + Node_t *InterlockedCompareExchangeNode( Node_t * volatile *ppNode, Node_t *value, Node_t *comperand ) + { + return (Node_t *)::ThreadInterlockedCompareExchangePointer( (void **)ppNode, value, comperand ); + } + + bool InterlockedCompareExchangeNodeLink( NodeLink_t volatile *pLink, const NodeLink_t &value, const NodeLink_t &comperand ) + { + return ThreadInterlockedAssignIf64x128( &pLink->value64x128, value.value64x128, comperand.value64x128 ); + } + + NodeLink_t m_Head; + NodeLink_t m_Tail; + + CInterlockedInt m_Count; + + CTSListBase m_FreeNodes; +} TSLIST_NODE_ALIGN_POST; + +#if defined( _WIN32 ) +// Suppress this spurious warning: +// warning C4700: uninitialized local variable 'oldHead' used +#pragma warning( pop ) +#endif + +#endif // TSLIST_H diff --git a/public/tier0/type_traits.h b/public/tier0/type_traits.h new file mode 100644 index 0000000..6459de4 --- /dev/null +++ b/public/tier0/type_traits.h @@ -0,0 +1,32 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: functionality that is provided in C++11 type_traits, which +// currently isn't supported by the OSX compiler. Sadness. +// +//============================================================================= +#pragma once + +template <class T> +struct V_remove_const +{ + typedef T type; +}; + +template <class T> +struct V_remove_const<const T> +{ + typedef T type; +}; + +template <class T> +struct V_remove_const<const T[]> +{ + typedef T type; +}; + +template <class T, unsigned int N> +struct V_remove_const<const T[N]> +{ + typedef T type; +}; + diff --git a/public/tier0/validator.h b/public/tier0/validator.h new file mode 100644 index 0000000..6af0f51 --- /dev/null +++ b/public/tier0/validator.h @@ -0,0 +1,73 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +//=============================================================================// + + +#include "valobject.h" + +#ifndef VALIDATOR_H +#define VALIDATOR_H + +#ifdef _WIN32 +#pragma once +#endif + + +#ifdef DBGFLAG_VALIDATE + + +class CValidator +{ +public: + // Constructors & destructors + CValidator( void ); + ~CValidator( void ); + + // Call this each time we enter a new Validate function + void Push( tchar *pchType, void *pvObj, tchar *pchName ); + + // Call this each time we exit a Validate function + void Pop( void ); + + // Claim ownership of a memory block + void ClaimMemory( void *pvMem ); + + // Finish performing a check and perform necessary computations + void Finalize( void ); + + // Render our results to the console + void RenderObjects( int cubThreshold ); // Render all reported objects + void RenderLeaks( void ); // Render all memory leaks + + // List manipulation functions: + CValObject *FindObject( void *pvObj ); // Returns CValObject containing pvObj, or NULL. + void DiffAgainst( CValidator *pOtherValidator ); // Removes any entries from this validator that are also present in the other. + + // Accessors + bool BMemLeaks( void ) { return m_bMemLeaks; }; + CValObject *PValObjectFirst( void ) { return m_pValObjectFirst; }; + + void Validate( CValidator &validator, tchar *pchName ); // Validate our internal structures + + +private: + CValObject *m_pValObjectFirst; // Linked list of all ValObjects + CValObject *m_pValObjectLast; // Last ValObject on the linked list + + CValObject *m_pValObjectCur; // Object we're current processing + + int m_cpvOwned; // Total # of blocks owned + + int m_cpubLeaked; // # of leaked memory blocks + int m_cubLeaked; // Amount of leaked memory + bool m_bMemLeaks; // Has any memory leaked? +}; + + +#endif // DBGFLAG_VALIDATE + + +#endif // VALIDATOR_H diff --git a/public/tier0/valobject.h b/public/tier0/valobject.h new file mode 100644 index 0000000..c139e9e --- /dev/null +++ b/public/tier0/valobject.h @@ -0,0 +1,72 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: CValObject is used for tracking individual objects that report +// in to CValidator. Whenever a new object reports in (via CValidator::Push), +// we create a new CValObject to aggregate stats for it. +// +// $NoKeywords: $ +//=============================================================================// + +#ifndef VALOBJECT_H +#define VALOBJECT_H +#ifdef _WIN32 +#pragma once +#endif + + +#ifdef DBGFLAG_VALIDATE +class CValObject +{ +public: + // Constructors & destructors + CValObject( void ) { }; + ~CValObject( void ); + + void Init( tchar *pchType, void *pvObj, tchar *pchName, CValObject *pValObjectParent, + CValObject *pValObjectPrev ); + + // Our object has claimed ownership of a memory block + void ClaimMemoryBlock( void *pvMem ); + + // A child of ours has claimed ownership of a memory block + void ClaimChildMemoryBlock( int cubUser ); + + // Accessors + tchar *PchType( void ) { return m_rgchType; }; + void *PvObj( void ) { return m_pvObj; }; + tchar *PchName( void ) { return m_rgchName; }; + CValObject *PValObjectParent( void ) { return m_pValObjectParent; }; + int NLevel( void ) { return m_nLevel; }; + CValObject *PValObjectNext( void ) { return m_pValObjectNext; }; + int CpubMemSelf( void ) { return m_cpubMemSelf; }; + int CubMemSelf( void ) { return m_cubMemSelf; }; + int CpubMemTree( void ) { return m_cpubMemTree; }; + int CubMemTree( void ) { return m_cubMemTree; }; + int NUser( void ) { return m_nUser; }; + void SetNUser( int nUser ) { m_nUser = nUser; }; + void SetBNewSinceSnapshot( bool bNewSinceSnapshot ) { m_bNewSinceSnapshot = bNewSinceSnapshot; } + bool BNewSinceSnapshot( void ) { return m_bNewSinceSnapshot; } + +private: + bool m_bNewSinceSnapshot; // If this block is new since the snapshot. + tchar m_rgchType[64]; // Type of the object we represent + tchar m_rgchName[64]; // Name of this particular object + void *m_pvObj; // Pointer to the object we represent + + CValObject *m_pValObjectParent; // Our parent object in the tree. + int m_nLevel; // Our depth in the tree + + CValObject *m_pValObjectNext; // Next ValObject in the linked list + + int m_cpubMemSelf; // # of memory blocks we own directly + int m_cubMemSelf; // Total size of the memory blocks we own directly + + int m_cpubMemTree; // # of memory blocks owned by us and our children + int m_cubMemTree; // Total size of the memory blocks owned by us and our children + + int m_nUser; // Field provided for use by our users +}; +#endif // DBGFLAG_VALIDATE + + +#endif // VALOBJECT_H diff --git a/public/tier0/valve_minmax_off.h b/public/tier0/valve_minmax_off.h new file mode 100644 index 0000000..633a378 --- /dev/null +++ b/public/tier0/valve_minmax_off.h @@ -0,0 +1,7 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +#ifdef min + #undef min +#endif +#ifdef max + #undef max +#endif diff --git a/public/tier0/valve_minmax_on.h b/public/tier0/valve_minmax_on.h new file mode 100644 index 0000000..738eabc --- /dev/null +++ b/public/tier0/valve_minmax_on.h @@ -0,0 +1,9 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +#if !defined(POSIX) +#ifndef min + #define min(a,b) (((a) < (b)) ? (a) : (b)) +#endif +#ifndef max + #define max(a,b) (((a) > (b)) ? (a) : (b)) +#endif +#endif diff --git a/public/tier0/valve_off.h b/public/tier0/valve_off.h new file mode 100644 index 0000000..796408e --- /dev/null +++ b/public/tier0/valve_off.h @@ -0,0 +1,30 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: This turns off all Valve-specific #defines. Because we sometimes +// call external include files from inside .cpp files, we need to +// wrap those includes like this: +// #include "tier0/valve_off.h" +// #include <external.h> +// #include "tier0/valve_on.h" +// +// $NoKeywords: $ +//=============================================================================// + + +#ifdef STEAM + +//----------------------------------------------------------------------------- +// Unicode-related #defines (see wchartypes.h) +//----------------------------------------------------------------------------- +#undef char + + +//----------------------------------------------------------------------------- +// Memory-related #defines +//----------------------------------------------------------------------------- +#undef malloc +#undef realloc +#undef _expand +#undef free + +#endif diff --git a/public/tier0/valve_on.h b/public/tier0/valve_on.h new file mode 100644 index 0000000..ffc9b89 --- /dev/null +++ b/public/tier0/valve_on.h @@ -0,0 +1,31 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: This turns on all Valve-specific #defines. Because we sometimes +// call external include files from inside .cpp files, we need to +// wrap those includes like this: +// #include "tier0/valve_off.h" +// #include <external.h> +// #include "tier0/valve_on.h" +// +// $NoKeywords: $ +//=============================================================================// + + +#ifdef STEAM +//----------------------------------------------------------------------------- +// Unicode-related #defines (see wchartypes.h) +//----------------------------------------------------------------------------- +#ifdef ENFORCE_WCHAR +#define char DontUseChar_SeeWcharOn.h +#endif + + +//----------------------------------------------------------------------------- +// Memory-related #defines +//----------------------------------------------------------------------------- +#define malloc( cub ) HEY_DONT_USE_MALLOC_USE_PVALLOC +#define realloc( pvOld, cub ) HEY_DONT_USE_REALLOC_USE_PVREALLOC +#define _expand( pvOld, cub ) HEY_DONT_USE_EXPAND_USE_PVEXPAND +#define free( pv ) HEY_DONT_USE_FREE_USE_FREEPV + +#endif diff --git a/public/tier0/vcr_shared.h b/public/tier0/vcr_shared.h new file mode 100644 index 0000000..d2d5ac7 --- /dev/null +++ b/public/tier0/vcr_shared.h @@ -0,0 +1,54 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +//=============================================================================// + +#ifndef VCR_SHARED_H +#define VCR_SHARED_H +#ifdef _WIN32 +#pragma once +#endif + + +#define VCRFILE_VERSION 2 + + +// Identifiers for the things we record. When playing back, these things should +// be asked for in the exact same order (otherwise, the engine isn't making all +// the calls in the same order). +typedef enum +{ + VCREvent_Sys_FloatTime=0, + VCREvent_recvfrom, + VCREvent_SyncToken, + VCREvent_GetCursorPos, + VCREvent_SetCursorPos, + VCREvent_ScreenToClient, + VCREvent_Cmd_Exec, + VCREvent_CmdLine, + VCREvent_RegOpenKeyEx, + VCREvent_RegSetValueEx, + VCREvent_RegQueryValueEx, + VCREvent_RegCreateKeyEx, + VCREvent_RegCloseKey, + VCREvent_PeekMessage, + VCREvent_GameMsg, + VCREvent_GetNumberOfConsoleInputEvents, + VCREvent_ReadConsoleInput, + VCREvent_GetKeyState, + VCREvent_recv, + VCREvent_send, + VCREvent_Generic, + VCREvent_CreateThread, + VCREvent_WaitForSingleObject, + VCREvent_EnterCriticalSection, + VCREvent_Time, + VCREvent_LocalTime, + VCREvent_GenericString, + VCREvent_NUMEVENTS +} VCREvent; + + +#endif // VCR_SHARED_H diff --git a/public/tier0/vcrmode.h b/public/tier0/vcrmode.h new file mode 100644 index 0000000..2af8a60 --- /dev/null +++ b/public/tier0/vcrmode.h @@ -0,0 +1,306 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: VCR mode records a client's game and allows you to +// play it back and reproduce it exactly. When playing it back, nothing +// is simulated on the server, but all server packets are recorded. +// +// Most of the VCR mode functionality is accomplished through hooks +// called at various points in the engine. +// +// $NoKeywords: $ +//===========================================================================// +#ifndef VCRMODE_H +#define VCRMODE_H + +#ifdef _WIN32 +#include <process.h> +#endif + +#ifdef _WIN32 +#pragma once +#endif + +#include "tier0/platform.h" +#include "tier0/vcr_shared.h" +#include "tier0/dbg.h" + +#ifdef POSIX +DBG_INTERFACE const char *BuildCmdLine( int argc, char **argv, bool fAddSteam = true ); +tchar *GetCommandLine(); +#endif + +#ifdef _X360 +#define NO_VCR 1 +#endif + + +// Enclose lines of code in this if you don't want anything in them written to or read from the VCR file. +#ifndef NO_VCR +#define NOVCR(x) \ +{\ + VCRSetEnabled(0);\ + x;\ + VCRSetEnabled(1);\ +} +#else +#define NOVCR(x) \ +{\ + x;\ +} +#endif + + +//----------------------------------------------------------------------------- +// Forward declarations +//----------------------------------------------------------------------------- +struct InputEvent_t; + + +//----------------------------------------------------------------------------- +// Definitions. +//----------------------------------------------------------------------------- +enum VCRMode_t +{ + VCR_Invalid=-1, + VCR_Disabled=0, + VCR_Record, + VCR_Playback +}; + + +//----------------------------------------------------------------------------- +// Functions. +//----------------------------------------------------------------------------- +abstract_class IVCRHelpers +{ +public: + virtual void ErrorMessage( const tchar *pMsg ) = 0; + virtual void* GetMainWindow() = 0; +}; + + +// Used by the vcrtrace program. +abstract_class IVCRTrace +{ +public: + virtual VCREvent ReadEvent() = 0; + virtual void Read( void *pDest, int size ) = 0; +}; + +typedef struct VCR_s +{ + // Start VCR record or play. + int (*Start)( tchar const *pFilename, bool bRecord, IVCRHelpers *pHelpers ); + void (*End)(); + + // Used by the VCR trace app. + IVCRTrace* (*GetVCRTraceInterface)(); + + // Get the current mode the VCR is in. + VCRMode_t (*GetMode)(); + + // This can be used to block out areas of code that are unpredictable (like things triggered by WM_TIMER messages). + // Note: this enables/disables VCR mode usage on a PER-THREAD basis. The assumption is that you're marking out + // specific sections of code that you don't want to use VCR mode inside of, but you're not intending to + // stop all the other threads from using VCR mode. + void (*SetEnabled)(int bEnabled); + + // This can be called any time to put in a debug check to make sure things are synchronized. + void (*SyncToken)(tchar const *pToken); + + // Hook for Sys_FloatTime(). + double (*Hook_Sys_FloatTime)(double time); + + // Note: this makes no guarantees about msg.hwnd being the same on playback. If it needs to be, then we need to add + // an ID system for Windows and store the ID like in Goldsrc. + int (*Hook_PeekMessage)( + struct tagMSG *msg, + void *hWnd, + unsigned int wMsgFilterMin, + unsigned int wMsgFilterMax, + unsigned int wRemoveMsg + ); + + // Call this to record game messages. + void (*Hook_RecordGameMsg)( const InputEvent_t &event ); + void (*Hook_RecordEndGameMsg)(); + + // Call this to playback game messages until it returns false. + bool (*Hook_PlaybackGameMsg)( InputEvent_t *pEvent ); + + // Hook for recvfrom() calls. This replaces the recvfrom() call. + int (*Hook_recvfrom)(int s, char *buf, int len, int flags, struct sockaddr *from, int *fromlen); + + void (*Hook_GetCursorPos)(struct tagPOINT *pt); + void (*Hook_ScreenToClient)(void *hWnd, struct tagPOINT *pt); + + void (*Hook_Cmd_Exec)(tchar **f); + + tchar* (*Hook_GetCommandLine)(); + + // Registry hooks. + long (*Hook_RegOpenKeyEx)( void *hKey, const tchar *lpSubKey, unsigned long ulOptions, unsigned long samDesired, void *pHKey ); + long (*Hook_RegSetValueEx)(void *hKey, tchar const *lpValueName, unsigned long Reserved, unsigned long dwType, uint8 const *lpData, unsigned long cbData); + long (*Hook_RegQueryValueEx)(void *hKey, tchar const *lpValueName, unsigned long *lpReserved, unsigned long *lpType, uint8 *lpData, unsigned long *lpcbData); + long (*Hook_RegCreateKeyEx)(void *hKey, tchar const *lpSubKey, unsigned long Reserved, tchar *lpClass, unsigned long dwOptions, unsigned long samDesired, void *lpSecurityAttributes, void *phkResult, unsigned long *lpdwDisposition); + void (*Hook_RegCloseKey)(void *hKey); + + // hInput is a HANDLE. + int (*Hook_GetNumberOfConsoleInputEvents)( void *hInput, unsigned long *pNumEvents ); + + // hInput is a HANDLE. + // pRecs is an INPUT_RECORD pointer. + int (*Hook_ReadConsoleInput)( void *hInput, void *pRecs, int nMaxRecs, unsigned long *pNumRead ); + + + // This calls time() then gives you localtime()'s result. + void (*Hook_LocalTime)( struct tm *today ); + + short (*Hook_GetKeyState)( int nVirtKey ); + + // TCP calls. + int (*Hook_recv)( int s, char *buf, int len, int flags ); + int (*Hook_send)( int s, const char *buf, int len, int flags ); + + // These can be used to add events without having to modify VCR mode. + // pEventName is used for verification to make sure it's playing back correctly. + // If pEventName is null, then verification is not performed. + void (*GenericRecord)( const tchar *pEventName, const void *pData, int len ); + + + // Returns the number of bytes written in the generic event. + // If bForceLenSame is true, then it will error out unless the value in the VCR file is the same as maxLen. + int (*GenericPlayback)( const tchar *pEventName, void *pOutData, int maxLen, bool bForceLenSame ); + + // If you just want to record and playback a value and not worry about whether or not you're + // recording or playing back, use this. It also will do nothing if you're not recording or playing back. + // + // NOTE: also see GenericValueVerify, which allows you to have it VERIFY that pData's contents are the same upon playback + // (rather than just copying whatever is in the VCR file into pData). + void (*GenericValue)( const tchar *pEventName, void *pData, int maxLen ); + + // Get the current percent (0.0 - 1.0) that it's played back through the file (only valid in playback). + double (*GetPercentCompleted)(); + + // If you use this, then any VCR stuff the thread does will work with VCR mode. + // This mirrors the Windows API CreateThread function and returns a HANDLE the same way. + void* (*Hook_CreateThread)( + void *lpThreadAttributes, + unsigned long dwStackSize, + void *lpStartAddress, + void *lpParameter, + unsigned long dwCreationFlags, + unsigned long *lpThreadID ); + + unsigned long (*Hook_WaitForSingleObject)( + void *handle, + unsigned long dwMilliseconds ); + + void (*Hook_EnterCriticalSection)( void *pCS ); + + void (*Hook_Time)( long *pTime ); + + // String value. Playback just verifies that the incoming string is the same as it was when recording. + void (*GenericString)( const char *pEventName, const char *pString ); + + // Works like GenericValue, except upon playback it will verify that pData's contents are the same as it was during recording. + void (*GenericValueVerify)( const tchar *pEventName, const void *pData, int maxLen ); + + unsigned long (*Hook_WaitForMultipleObjects)( uint32 nHandles, const void **pHandles, int bWaitAll, uint32 timeout ); + +} VCR_t; + +#ifndef NO_VCR + +// In the launcher, this is created by vcrmode.c. +// In the engine, this is set when the launcher initializes its DLL. +PLATFORM_INTERFACE VCR_t *g_pVCR; + +#endif + + +#ifndef NO_VCR +#define VCRStart g_pVCR->Start +#define VCREnd g_pVCR->End +#define VCRGetVCRTraceInterface g_pVCR->GetVCRTraceInterface +#define VCRGetMode g_pVCR->GetMode +#define VCRSetEnabled g_pVCR->SetEnabled +#define VCRSyncToken g_pVCR->SyncToken +#define VCRGenericString g_pVCR->GenericString +#define VCRGenericValueVerify g_pVCR->GenericValueVerify +#define VCRHook_Sys_FloatTime g_pVCR->Hook_Sys_FloatTime +#define VCRHook_PeekMessage g_pVCR->Hook_PeekMessage +#define VCRHook_RecordGameMsg g_pVCR->Hook_RecordGameMsg +#define VCRHook_RecordEndGameMsg g_pVCR->Hook_RecordEndGameMsg +#define VCRHook_PlaybackGameMsg g_pVCR->Hook_PlaybackGameMsg +#define VCRHook_recvfrom g_pVCR->Hook_recvfrom +#define VCRHook_GetCursorPos g_pVCR->Hook_GetCursorPos +#define VCRHook_ScreenToClient g_pVCR->Hook_ScreenToClient +#define VCRHook_Cmd_Exec g_pVCR->Hook_Cmd_Exec +#define VCRHook_GetCommandLine g_pVCR->Hook_GetCommandLine +#define VCRHook_RegOpenKeyEx g_pVCR->Hook_RegOpenKeyEx +#define VCRHook_RegSetValueEx g_pVCR->Hook_RegSetValueEx +#define VCRHook_RegQueryValueEx g_pVCR->Hook_RegQueryValueEx +#define VCRHook_RegCreateKeyEx g_pVCR->Hook_RegCreateKeyEx +#define VCRHook_RegCloseKey g_pVCR->Hook_RegCloseKey +#define VCRHook_GetNumberOfConsoleInputEvents g_pVCR->Hook_GetNumberOfConsoleInputEvents +#define VCRHook_ReadConsoleInput g_pVCR->Hook_ReadConsoleInput +#define VCRHook_LocalTime g_pVCR->Hook_LocalTime +#define VCRHook_GetKeyState g_pVCR->Hook_GetKeyState +#define VCRHook_recv g_pVCR->Hook_recv +#define VCRHook_send g_pVCR->Hook_send +#define VCRGenericRecord g_pVCR->GenericRecord +#define VCRGenericPlayback g_pVCR->GenericPlayback +#define VCRGenericValue g_pVCR->GenericValue +#define VCRGetPercentCompleted g_pVCR->GetPercentCompleted +#define VCRHook_CreateThread g_pVCR->Hook_CreateThread +#define VCRHook_WaitForSingleObject g_pVCR->Hook_WaitForSingleObject +#define VCRHook_EnterCriticalSection g_pVCR->Hook_EnterCriticalSection +#define VCRHook_Time g_pVCR->Hook_Time +#define VCRHook_WaitForMultipleObjects( a, b, c, d) g_pVCR->Hook_WaitForMultipleObjects( a, (const void **)b, c, d) +#else +#define VCRStart( a, b, c ) (1) +#define VCREnd ((void)(0)) +#define VCRGetVCRTraceInterface (NULL) +#define VCRGetMode() (VCR_Disabled) +#define VCRSetEnabled( a ) ((void)(0)) +#define VCRSyncToken( a ) ((void)(0)) +#define VCRGenericRecord MUST_IFDEF_OUT_GenericRecord +#define VCRGenericPlayback MUST_IFDEF_OUT_GenericPlayback +#define VCRGenericValue MUST_IFDEF_OUT_GenericValue +#define VCRGenericString MUST_IFDEF_OUT_GenericString +#define VCRGenericValueVerify MUST_IFDEF_OUT_GenericValueVerify +#define VCRGetPercentCompleted() (0.0f) +#define VCRHook_Sys_FloatTime Sys_FloatTime +#define VCRHook_PeekMessage PeekMessage +#define VCRHook_RecordGameMsg RecordGameMsg +#define VCRHook_RecordEndGameMsg RecordEndGameMsg +#define VCRHook_PlaybackGameMsg PlaybackGameMsg +#define VCRHook_recvfrom recvfrom +#define VCRHook_GetCursorPos GetCursorPos +#define VCRHook_ScreenToClient ScreenToClient +#define VCRHook_Cmd_Exec( a ) ((void)(0)) +#define VCRHook_GetCommandLine GetCommandLine +#define VCRHook_RegOpenKeyEx RegOpenKeyEx +#define VCRHook_RegSetValueEx RegSetValueEx +#define VCRHook_RegQueryValueEx RegQueryValueEx +#define VCRHook_RegCreateKeyEx RegCreateKeyEx +#define VCRHook_RegCloseKey RegCloseKey +#define VCRHook_GetNumberOfConsoleInputEvents GetNumberOfConsoleInputEvents +#define VCRHook_ReadConsoleInput ReadConsoleInput +#define VCRHook_LocalTime( a ) memset(a, 0, sizeof(*a)); +#define VCRHook_GetKeyState GetKeyState +#define VCRHook_recv recv +#define VCRHook_send send +#if defined( _X360 ) +#define VCRHook_CreateThread CreateThread +#else +#define VCRHook_CreateThread (void*)_beginthreadex +#endif +#define VCRHook_WaitForSingleObject WaitForSingleObject +#define VCRHook_EnterCriticalSection EnterCriticalSection +#define VCRHook_WaitForMultipleObjects( a, b, c, d) WaitForMultipleObjects( a, (const HANDLE *)b, c, d) +#define VCRHook_Time Time +#endif + +#endif // VCRMODE_H diff --git a/public/tier0/vprof.h b/public/tier0/vprof.h new file mode 100644 index 0000000..1c7321b --- /dev/null +++ b/public/tier0/vprof.h @@ -0,0 +1,1440 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: Real-Time Hierarchical Profiling +// +// $NoKeywords: $ +//=============================================================================// + +#ifndef VPROF_H +#define VPROF_H + +#include "tier0/dbg.h" +#include "tier0/fasttimer.h" +#include "tier0/l2cache.h" +#include "tier0/threadtools.h" +#include "tier0/vprof_telemetry.h" + +// VProf is enabled by default in all configurations -except- X360 Retail. +#if !( defined( _X360 ) && defined( _CERT ) ) +#define VPROF_ENABLED +#endif + +#if defined(_X360) && defined(VPROF_ENABLED) +#include "tier0/pmc360.h" +#ifndef USE_PIX +#define VPROF_UNDO_PIX +#undef _PIX_H_ +#undef PIXBeginNamedEvent +#undef PIXEndNamedEvent +#undef PIXSetMarker +#undef PIXNameThread +#define USE_PIX +#include <pix.h> +#undef USE_PIX +#else +#include <pix.h> +#endif +#endif + +#ifdef _MSC_VER +#pragma warning(push) +#pragma warning(disable:4251) +#endif + +// enable this to get detailed nodes beneath budget +// #define VPROF_LEVEL 1 + +// enable this to use pix (360 only) +// #define VPROF_PIX 1 + +#if defined(VPROF_PIX) +#pragma comment( lib, "Xapilibi" ) +#endif + +//----------------------------------------------------------------------------- +// +// Profiling instrumentation macros +// + +#define MAXCOUNTERS 256 + + +#ifdef VPROF_ENABLED + +#define VPROF_VTUNE_GROUP + +#define VPROF( name ) VPROF_(name, 1, VPROF_BUDGETGROUP_OTHER_UNACCOUNTED, false, 0) +#define VPROF_ASSERT_ACCOUNTED( name ) VPROF_(name, 1, VPROF_BUDGETGROUP_OTHER_UNACCOUNTED, true, 0) +#define VPROF_( name, detail, group, bAssertAccounted, budgetFlags ) VPROF_##detail(name,group, bAssertAccounted, budgetFlags) + +#define VPROF_BUDGET( name, group ) VPROF_BUDGET_FLAGS(name, group, BUDGETFLAG_OTHER) +#define VPROF_BUDGET_FLAGS( name, group, flags ) VPROF_(name, 0, group, false, flags) + +#define VPROF_SCOPE_BEGIN( tag ) do { VPROF( tag ) +#define VPROF_SCOPE_END() } while (0) + +#define VPROF_ONLY( expression ) expression + +#define VPROF_ENTER_SCOPE( name ) g_VProfCurrentProfile.EnterScope( name, 1, VPROF_BUDGETGROUP_OTHER_UNACCOUNTED, false, 0 ) +#define VPROF_EXIT_SCOPE() g_VProfCurrentProfile.ExitScope() + +#define VPROF_BUDGET_GROUP_ID_UNACCOUNTED 0 + + +// Budgetgroup flags. These are used with VPROF_BUDGET_FLAGS. +// These control which budget panels the groups show up in. +// If a budget group uses VPROF_BUDGET, it gets the default +// which is BUDGETFLAG_OTHER. +#define BUDGETFLAG_CLIENT (1<<0) // Shows up in the client panel. +#define BUDGETFLAG_SERVER (1<<1) // Shows up in the server panel. +#define BUDGETFLAG_OTHER (1<<2) // Unclassified (the client shows these but the dedicated server doesn't). +#define BUDGETFLAG_HIDDEN (1<<15) +#define BUDGETFLAG_ALL 0xFFFF + + +// NOTE: You can use strings instead of these defines. . they are defined here and added +// in vprof.cpp so that they are always in the same order. +#define VPROF_BUDGETGROUP_OTHER_UNACCOUNTED _T("Unaccounted") +#define VPROF_BUDGETGROUP_WORLD_RENDERING _T("World Rendering") +#define VPROF_BUDGETGROUP_DISPLACEMENT_RENDERING _T("Displacement_Rendering") +#define VPROF_BUDGETGROUP_GAME _T("Game") +#define VPROF_BUDGETGROUP_NPCS _T("NPCs") +#define VPROF_BUDGETGROUP_SERVER_ANIM _T("Server Animation") +#define VPROF_BUDGETGROUP_PHYSICS _T("Physics") +#define VPROF_BUDGETGROUP_STATICPROP_RENDERING _T("Static_Prop_Rendering") +#define VPROF_BUDGETGROUP_MODEL_RENDERING _T("Other_Model_Rendering") +#define VPROF_BUDGETGROUP_MODEL_FAST_PATH_RENDERING _T("Fast Path Model Rendering") +#define VPROF_BUDGETGROUP_BRUSHMODEL_RENDERING _T("Brush_Model_Rendering") +#define VPROF_BUDGETGROUP_SHADOW_RENDERING _T("Shadow_Rendering") +#define VPROF_BUDGETGROUP_DETAILPROP_RENDERING _T("Detail_Prop_Rendering") +#define VPROF_BUDGETGROUP_PARTICLE_RENDERING _T("Particle/Effect_Rendering") +#define VPROF_BUDGETGROUP_ROPES _T("Ropes") +#define VPROF_BUDGETGROUP_DLIGHT_RENDERING _T("Dynamic_Light_Rendering") +#define VPROF_BUDGETGROUP_OTHER_NETWORKING _T("Networking") +#define VPROF_BUDGETGROUP_CLIENT_ANIMATION _T("Client_Animation") +#define VPROF_BUDGETGROUP_OTHER_SOUND _T("Sound") +#define VPROF_BUDGETGROUP_OTHER_VGUI _T("VGUI") +#define VPROF_BUDGETGROUP_OTHER_FILESYSTEM _T("FileSystem") +#define VPROF_BUDGETGROUP_PREDICTION _T("Prediction") +#define VPROF_BUDGETGROUP_INTERPOLATION _T("Interpolation") +#define VPROF_BUDGETGROUP_SWAP_BUFFERS _T("Swap_Buffers") +#define VPROF_BUDGETGROUP_PLAYER _T("Player") +#define VPROF_BUDGETGROUP_OCCLUSION _T("Occlusion") +#define VPROF_BUDGETGROUP_OVERLAYS _T("Overlays") +#define VPROF_BUDGETGROUP_TOOLS _T("Tools") +#define VPROF_BUDGETGROUP_LIGHTCACHE _T("Light_Cache") +#define VPROF_BUDGETGROUP_DISP_HULLTRACES _T("Displacement_Hull_Traces") +#define VPROF_BUDGETGROUP_TEXTURE_CACHE _T("Texture_Cache") +#define VPROF_BUDGETGROUP_REPLAY _T("Replay") +#define VPROF_BUDGETGROUP_PARTICLE_SIMULATION _T("Particle Simulation") +#define VPROF_BUDGETGROUP_SHADOW_DEPTH_TEXTURING _T("Flashlight Shadows") +#define VPROF_BUDGETGROUP_CLIENT_SIM _T("Client Simulation") // think functions, tempents, etc. +#define VPROF_BUDGETGROUP_STEAM _T("Steam") +#define VPROF_BUDGETGROUP_CVAR_FIND _T("Cvar_Find") +#define VPROF_BUDGETGROUP_CLIENTLEAFSYSTEM _T("ClientLeafSystem") +#define VPROF_BUDGETGROUP_JOBS_COROUTINES _T("Jobs/Coroutines") +#define VPROF_BUDGETGROUP_SLEEPING _T("Sleeping") +#define VPROF_BUDGETGROUP_THREADINGMAIN _T("ThreadingMain") +#define VPROF_BUDGETGROUP_HTMLSURFACE _T("HTMLSurface") +#define VPROF_BUDGETGROUP_VGUI VPROF_BUDGETGROUP_HTMLSURFACE +#define VPROF_BUDGETGROUP_TENFOOT VPROF_BUDGETGROUP_HTMLSURFACE +#define VPROF_BUDGETGROUP_STEAMUI VPROF_BUDGETGROUP_HTMLSURFACE +#define VPROF_BUDGETGROUP_ATTRIBUTES _T("Attributes") +#define VPROF_BUDGETGROUP_FINDATTRIBUTE _T("FindAttribute") +#define VPROF_BUDGETGROUP_FINDATTRIBUTEUNSAFE _T("FindAttributeUnsafe") + +#ifdef _X360 +// update flags +#define VPROF_UPDATE_BUDGET 0x01 // send budget data every frame +#define VPROF_UPDATE_TEXTURE_GLOBAL 0x02 // send global texture data every frame +#define VPROF_UPDATE_TEXTURE_PERFRAME 0x04 // send perframe texture data every frame +#endif + +//------------------------------------- + +#ifndef VPROF_LEVEL +#define VPROF_LEVEL 0 +#endif + +//these macros exist to create VProf_<line number> variables. This is important because it avoids /analyze warnings about variable aliasing when VPROF's are nested within each other, and allows +//for multiple VPROF's to exist within the same scope. Three macros must be used to force the __LINE__ to be resolved prior to the token concatenation, but just ignore the _INTERNAL macros and use +//the VPROF_VAR_NAME +#define VPROF_VAR_NAME_INTERNAL_CAT(a, b) a##b +#define VPROF_VAR_NAME_INTERNAL( a, b ) VPROF_VAR_NAME_INTERNAL_CAT( a, b ) +#define VPROF_VAR_NAME( a ) VPROF_VAR_NAME_INTERNAL( a, __LINE__ ) + +#define VPROF_0(name,group,assertAccounted,budgetFlags) tmZone( TELEMETRY_LEVEL2, TMZF_NONE, "(%s)%s", group, name ); CVProfScope VPROF_VAR_NAME( VProf_ )(name, 0, group, assertAccounted, budgetFlags); + +#if VPROF_LEVEL > 0 +#define VPROF_1(name,group,assertAccounted,budgetFlags) tmZone( TELEMETRY_LEVEL3, TMZF_NONE, "(%s)%s", group, name ); CVProfScope VPROF_VAR_NAME( VProf_ )(name, 1, group, assertAccounted, budgetFlags); +#else +#define VPROF_1(name,group,assertAccounted,budgetFlags) ((void)0) +#endif + +#if VPROF_LEVEL > 1 +#define VPROF_2(name,group,assertAccounted,budgetFlags) CVProfScope VPROF_VAR_NAME( VProf_ )(name, 2, group, assertAccounted, budgetFlags); +#else +#define VPROF_2(name,group,assertAccounted,budgetFlags) ((void)0) +#endif + +#if VPROF_LEVEL > 2 +#define VPROF_3(name,group,assertAccounted,budgetFlags) CVProfScope VPROF_VAR_NAME( VProf_ )(name, 3, group, assertAccounted, budgetFlags); +#else +#define VPROF_3(name,group,assertAccounted,budgetFlags) ((void)0) +#endif + +#if VPROF_LEVEL > 3 +#define VPROF_4(name,group,assertAccounted,budgetFlags) CVProfScope VPROF_VAR_NAME( VProf_ )(name, 4, group, assertAccounted, budgetFlags); +#else +#define VPROF_4(name,group,assertAccounted,budgetFlags) ((void)0) +#endif + +//------------------------------------- + +#ifdef _MSC_VER +#define VProfCode( code ) \ + if ( 0 ) \ + ; \ + else \ + { \ + VPROF( __FUNCTION__ ": " #code ); \ + code; \ + } +#else +#define VProfCode( code ) \ + if ( 0 ) \ + ; \ + else \ + { \ + VPROF( #code ); \ + code; \ + } +#endif + + +//------------------------------------- + +#define VPROF_INCREMENT_COUNTER(name,amount) do { static CVProfCounter _counter( name ); _counter.Increment( amount ); } while( 0 ) +#define VPROF_INCREMENT_GROUP_COUNTER(name,group,amount) do { static CVProfCounter _counter( name, group ); _counter.Increment( amount ); } while( 0 ) + +#else + +#define VPROF( name ) ((void)0) +#define VPROF_ASSERT_ACCOUNTED( name ) ((void)0) +#define VPROF_( name, detail, group, bAssertAccounted, budgetFlags ) ((void)0) +#define VPROF_BUDGET( name, group ) ((void)0) +#define VPROF_BUDGET_FLAGS( name, group, flags ) ((void)0) + +#define VPROF_SCOPE_BEGIN( tag ) do { +#define VPROF_SCOPE_END() } while (0) + +#define VPROF_ONLY( expression ) ((void)0) + +#define VPROF_ENTER_SCOPE( name ) +#define VPROF_EXIT_SCOPE() + +#define VPROF_INCREMENT_COUNTER(name,amount) ((void)0) +#define VPROF_INCREMENT_GROUP_COUNTER(name,group,amount) ((void)0) + +#define VPROF_TEST_SPIKE( msec ) ((void)0) + +#define VProfCode( code ) code + +#endif + +//----------------------------------------------------------------------------- + +#ifdef VPROF_ENABLED + +//----------------------------------------------------------------------------- +// +// A node in the call graph hierarchy +// + +class DBG_CLASS CVProfNode +{ +friend class CVProfRecorder; +friend class CVProfile; + +public: + CVProfNode( const tchar * pszName, int detailLevel, CVProfNode *pParent, const tchar *pBudgetGroupName, int budgetFlags ); + ~CVProfNode(); + + CVProfNode *GetSubNode( const tchar *pszName, int detailLevel, const tchar *pBudgetGroupName, int budgetFlags ); + CVProfNode *GetSubNode( const tchar *pszName, int detailLevel, const tchar *pBudgetGroupName ); + CVProfNode *GetParent(); + CVProfNode *GetSibling(); + CVProfNode *GetPrevSibling(); + CVProfNode *GetChild(); + + void MarkFrame(); + void ResetPeak(); + + void Pause(); + void Resume(); + void Reset(); + + void EnterScope(); + bool ExitScope(); + + const tchar *GetName(); + + int GetBudgetGroupID() + { + return m_BudgetGroupID; + } + + // Only used by the record/playback stuff. + void SetBudgetGroupID( int id ) + { + m_BudgetGroupID = id; + } + + int GetCurCalls(); + double GetCurTime(); + int GetPrevCalls(); + double GetPrevTime(); + int GetTotalCalls(); + double GetTotalTime(); + double GetPeakTime(); + + double GetCurTimeLessChildren(); + double GetPrevTimeLessChildren(); + double GetTotalTimeLessChildren(); + + int GetPrevL2CacheMissLessChildren(); + int GetPrevLoadHitStoreLessChildren(); + + void ClearPrevTime(); + + int GetL2CacheMisses(); + + // Not used in the common case... + void SetCurFrameTime( unsigned long milliseconds ); + + void SetClientData( int iClientData ) { m_iClientData = iClientData; } + int GetClientData() const { return m_iClientData; } + +#ifdef DBGFLAG_VALIDATE + void Validate( CValidator &validator, tchar *pchName ); // Validate our internal structures +#endif // DBGFLAG_VALIDATE + + +// Used by vprof record/playback. +private: + + void SetUniqueNodeID( int id ) + { + m_iUniqueNodeID = id; + } + + int GetUniqueNodeID() const + { + return m_iUniqueNodeID; + } + + static int s_iCurrentUniqueNodeID; + + +private: + const tchar *m_pszName; + CFastTimer m_Timer; + + // L2 Cache data. + int m_iPrevL2CacheMiss; + int m_iCurL2CacheMiss; + int m_iTotalL2CacheMiss; + +#ifndef _X360 + // L2 Cache data. + CL2Cache m_L2Cache; +#else // 360: + + unsigned int m_iBitFlags; // see enum below for settings + CPMCData m_PMCData; + int m_iPrevLoadHitStores; + int m_iCurLoadHitStores; + int m_iTotalLoadHitStores; + + public: + enum FlagBits + { + kRecordL2 = 0x01, + kCPUTrace = 0x02, ///< cause a PIX trace inside this node. + }; + // call w/ true to enable L2 and LHS recording; false to turn it off + inline void EnableL2andLHS(bool enable) + { + if (enable) + m_iBitFlags |= kRecordL2; + else + m_iBitFlags &= (~kRecordL2); + } + + inline bool IsL2andLHSEnabled( void ) + { + return (m_iBitFlags & kRecordL2) != 0; + } + + int GetLoadHitStores(); + + private: + +#endif + + int m_nRecursions; + + unsigned m_nCurFrameCalls; + CCycleCount m_CurFrameTime; + + unsigned m_nPrevFrameCalls; + CCycleCount m_PrevFrameTime; + + unsigned m_nTotalCalls; + CCycleCount m_TotalTime; + + CCycleCount m_PeakTime; + + CVProfNode *m_pParent; + CVProfNode *m_pChild; + CVProfNode *m_pSibling; + + int m_BudgetGroupID; + + int m_iClientData; + int m_iUniqueNodeID; +}; + +//----------------------------------------------------------------------------- +// +// Coordinator and root node of the profile hierarchy tree +// + +enum VProfReportType_t +{ + VPRT_SUMMARY = ( 1 << 0 ), + VPRT_HIERARCHY = ( 1 << 1 ), + VPRT_HIERARCHY_TIME_PER_FRAME_AND_COUNT_ONLY = ( 1 << 2 ), + VPRT_LIST_BY_TIME = ( 1 << 3 ), + VPRT_LIST_BY_TIME_LESS_CHILDREN = ( 1 << 4 ), + VPRT_LIST_BY_AVG_TIME = ( 1 << 5 ), + VPRT_LIST_BY_AVG_TIME_LESS_CHILDREN = ( 1 << 6 ), + VPRT_LIST_BY_PEAK_TIME = ( 1 << 7 ), + VPRT_LIST_BY_PEAK_OVER_AVERAGE = ( 1 << 8 ), + VPRT_LIST_TOP_ITEMS_ONLY = ( 1 << 9 ), + + VPRT_FULL = (0xffffffff & ~(VPRT_HIERARCHY_TIME_PER_FRAME_AND_COUNT_ONLY|VPRT_LIST_TOP_ITEMS_ONLY)), +}; + +enum CounterGroup_t +{ + COUNTER_GROUP_DEFAULT=0, + COUNTER_GROUP_NO_RESET, // The engine doesn't reset these counters. Usually, they are used + // like global variables that can be accessed across modules. + COUNTER_GROUP_TEXTURE_GLOBAL, // Global texture usage counters (totals for what is currently in memory). + COUNTER_GROUP_TEXTURE_PER_FRAME, // Per-frame texture usage counters. + + COUNTER_GROUP_TELEMETRY, +}; + +class DBG_CLASS CVProfile +{ +public: + CVProfile(); + ~CVProfile(); + + void Term(); + + // + // Runtime operations + // + + void Start(); + void Stop(); + + void SetTargetThreadId( unsigned id ) { m_TargetThreadId = id; } + unsigned GetTargetThreadId() { return m_TargetThreadId; } + bool InTargetThread() { return ( m_TargetThreadId == ThreadGetCurrentId() ); } + +#ifdef _X360 + enum VXConsoleReportMode_t + { + VXCONSOLE_REPORT_TIME = 0, + VXCONSOLE_REPORT_L2CACHE_MISSES, + VXCONSOLE_REPORT_LOAD_HIT_STORE, + VXCONSOLE_REPORT_COUNT, + }; + + void VXProfileStart(); + void VXProfileUpdate(); + void VXEnableUpdateMode( int event, bool bEnable ); + void VXSendNodes( void ); + + void PMCDisableAllNodes(CVProfNode *pStartNode = NULL); ///< turn off l2 and lhs recording for everywhere + bool PMCEnableL2Upon(const tchar *pszNodeName, bool bRecursive = false); ///< enable l2 and lhs recording for one given node + bool PMCDisableL2Upon(const tchar *pszNodeName, bool bRecursive = false); ///< enable l2 and lhs recording for one given node + + void DumpEnabledPMCNodes( void ); + + void VXConsoleReportMode( VXConsoleReportMode_t mode ); + void VXConsoleReportScale( VXConsoleReportMode_t mode, float flScale ); + + // the CPU trace mode is actually a small state machine; it can be off, primed for + // single capture, primed for everything-in-a-frame capture, or currently in everything-in-a-frame + // capture. + enum CPUTraceState + { + kDisabled, + kFirstHitNode, // record from the first time we hit the node until that node ends + kAllNodesInFrame_WaitingForMark, // we're going to record all the times a node is hit in a frame, but are waiting for the frame to start + kAllNodesInFrame_Recording, // we're recording all hits on a node this frame. + + // Same as above, but going to record for > 1 frame + kAllNodesInFrame_WaitingForMarkMultiFrame, // we're going to record all the times a node is hit in a frame, but are waiting for the frame to start + kAllNodesInFrame_RecordingMultiFrame, + }; + + // Global switch to turn CPU tracing on or off at all. The idea is you set up a node first, + // then trigger tracing by throwing this to true. It'll reset back to false after the trace + // happens. + inline CPUTraceState GetCPUTraceMode(); + inline void SetCPUTraceEnabled( CPUTraceState enabled, bool bTraceCompleteEvent = false, int nNumFrames = -1 ); + inline void IncrementMultiTraceIndex(); // tick up the counter that gets appended to the multi-per-frame traces + inline unsigned int GetMultiTraceIndex(); // return the counter + void CPUTraceDisableAllNodes( CVProfNode *pStartNode = NULL ); // disable the cpu trace flag wherever it may be + CVProfNode *CPUTraceEnableForNode( const tchar *pszNodeName ); // enable cpu trace on this node only, disabling it wherever else it may be on. + CVProfNode *CPUTraceGetEnabledNode( CVProfNode *pStartNode = NULL ); // return the node enabled for CPU tracing, or NULL. + const char *GetCPUTraceFilename(); // get the filename the trace should write into. + const char *SetCPUTraceFilename( const char *filename ); // set the filename the trace should write into. (don't specify the extension; I'll do that.) + inline bool TraceCompleteEvent( void ); + +#ifdef _X360 + void LatchMultiFrame( int64 cycles ); + void SpewWorstMultiFrame(); +#endif + +#endif + + void EnterScope( const tchar *pszName, int detailLevel, const tchar *pBudgetGroupName, bool bAssertAccounted ); + void EnterScope( const tchar *pszName, int detailLevel, const tchar *pBudgetGroupName, bool bAssertAccounted, int budgetFlags ); + void ExitScope(); + + void MarkFrame(); + void ResetPeaks(); + + void Pause(); + void Resume(); + void Reset(); + + bool IsEnabled() const; + int GetDetailLevel() const; + + bool AtRoot() const; + + // + // Queries + // + +#ifdef VPROF_VTUNE_GROUP +# define MAX_GROUP_STACK_DEPTH 1024 + + void EnableVTuneGroup( const tchar *pGroupName ) + { + m_nVTuneGroupID = BudgetGroupNameToBudgetGroupID( pGroupName ); + m_bVTuneGroupEnabled = true; + } + void DisableVTuneGroup( void ) + { + m_bVTuneGroupEnabled = false; + } + + inline void PushGroup( int nGroupID ); + inline void PopGroup( void ); +#endif + + int NumFramesSampled() { return m_nFrames; } + double GetPeakFrameTime(); + double GetTotalTimeSampled(); + double GetTimeLastFrame(); + + CVProfNode *GetRoot(); + CVProfNode *FindNode( CVProfNode *pStartNode, const tchar *pszNode ); + CVProfNode *GetCurrentNode(); + + typedef void ( __cdecl *StreamOut_t )( const char* pszFormat, ... ); + // Set the output function used for all vprof reports. Call this with NULL + // to set it to the default output function. + void SetOutputStream( StreamOut_t outputStream ); + void OutputReport( int type = VPRT_FULL, const tchar *pszStartNode = NULL, int budgetGroupID = -1 ); + + const tchar *GetBudgetGroupName( int budgetGroupID ); + int GetBudgetGroupFlags( int budgetGroupID ) const; // Returns a combination of BUDGETFLAG_ defines. + int GetNumBudgetGroups( void ); + void GetBudgetGroupColor( int budgetGroupID, int &r, int &g, int &b, int &a ); + int BudgetGroupNameToBudgetGroupID( const tchar *pBudgetGroupName ); + int BudgetGroupNameToBudgetGroupID( const tchar *pBudgetGroupName, int budgetFlagsToORIn ); + void RegisterNumBudgetGroupsChangedCallBack( void (*pCallBack)(void) ); + + int BudgetGroupNameToBudgetGroupIDNoCreate( const tchar *pBudgetGroupName ) { return FindBudgetGroupName( pBudgetGroupName ); } + + void HideBudgetGroup( int budgetGroupID, bool bHide = true ); + void HideBudgetGroup( const char *pszName, bool bHide = true ) { HideBudgetGroup( BudgetGroupNameToBudgetGroupID( pszName), bHide ); } + + int *FindOrCreateCounter( const tchar *pName, CounterGroup_t eCounterGroup=COUNTER_GROUP_DEFAULT ); + void ResetCounters( CounterGroup_t eCounterGroup ); + + int GetNumCounters( void ) const; + + const tchar *GetCounterName( int index ) const; + int GetCounterValue( int index ) const; + const tchar *GetCounterNameAndValue( int index, int &val ) const; + CounterGroup_t GetCounterGroup( int index ) const; + + // Performance monitoring events. + void PMEInitialized( bool bInit ) { m_bPMEInit = bInit; } + void PMEEnable( bool bEnable ) { m_bPMEEnabled = bEnable; } + +#ifndef _X360 + bool UsePME( void ) { return ( m_bPMEInit && m_bPMEEnabled ); } +#else + bool UsePME( void ) { return ( CPMCData::IsInitialized() && m_bPMEEnabled ); } +#endif + +#ifdef DBGFLAG_VALIDATE + void Validate( CValidator &validator, tchar *pchName ); // Validate our internal structures +#endif // DBGFLAG_VALIDATE + +protected: + + void FreeNodes_R( CVProfNode *pNode ); + +#ifdef VPROF_VTUNE_GROUP + bool VTuneGroupEnabled() + { + return m_bVTuneGroupEnabled; + } + int VTuneGroupID() + { + return m_nVTuneGroupID; + } +#endif + + void SumTimes( const tchar *pszStartNode, int budgetGroupID ); + void SumTimes( CVProfNode *pNode, int budgetGroupID ); + void DumpNodes( CVProfNode *pNode, int indent, bool bAverageAndCountOnly ); + int FindBudgetGroupName( const tchar *pBudgetGroupName ); + int AddBudgetGroupName( const tchar *pBudgetGroupName, int budgetFlags ); + +#ifdef VPROF_VTUNE_GROUP + bool m_bVTuneGroupEnabled; + int m_nVTuneGroupID; + int m_GroupIDStack[MAX_GROUP_STACK_DEPTH]; + int m_GroupIDStackDepth; +#endif + int m_enabled; + bool m_fAtRoot; // tracked for efficiency of the "not profiling" case + CVProfNode *m_pCurNode; + CVProfNode m_Root; + int m_nFrames; + int m_ProfileDetailLevel; + int m_pausedEnabledDepth; + + class CBudgetGroup + { + public: + tchar *m_pName; + int m_BudgetFlags; + }; + + CBudgetGroup *m_pBudgetGroups; + int m_nBudgetGroupNamesAllocated; + int m_nBudgetGroupNames; + void (*m_pNumBudgetGroupsChangedCallBack)(void); + + // Performance monitoring events. + bool m_bPMEInit; + bool m_bPMEEnabled; + + int m_Counters[MAXCOUNTERS]; + char m_CounterGroups[MAXCOUNTERS]; // (These are CounterGroup_t's). + tchar *m_CounterNames[MAXCOUNTERS]; + int m_NumCounters; + +#ifdef _X360 + int m_UpdateMode; + CPUTraceState m_iCPUTraceEnabled; + int m_nFramesRemaining; + int m_nFrameCount; + int64 m_WorstCycles; + char m_WorstTraceFilename[128]; + char m_CPUTraceFilename[128]; + unsigned int m_iSuccessiveTraceIndex; + VXConsoleReportMode_t m_ReportMode; + float m_pReportScale[VXCONSOLE_REPORT_COUNT]; + bool m_bTraceCompleteEvent; +#endif + + unsigned m_TargetThreadId; + + StreamOut_t m_pOutputStream; +}; + +//------------------------------------- + +DBG_INTERFACE CVProfile g_VProfCurrentProfile; + + +//----------------------------------------------------------------------------- + +DBG_INTERFACE bool g_VProfSignalSpike; + +class CVProfSpikeDetector +{ +public: + CVProfSpikeDetector( float spike ) : + m_timeLast( GetTimeLast() ) + { + m_spike = spike; + m_Timer.Start(); + } + + ~CVProfSpikeDetector() + { + m_Timer.End(); + if ( Plat_FloatTime() - m_timeLast > 2.0 ) + { + m_timeLast = Plat_FloatTime(); + if ( m_Timer.GetDuration().GetMillisecondsF() > m_spike ) + { + g_VProfSignalSpike = true; + } + } + } + +private: + static float &GetTimeLast() { static float timeLast = 0; return timeLast; } + CFastTimer m_Timer; + float m_spike; + float &m_timeLast; +}; + + +// Macro to signal a local spike. Meant as temporary instrumentation, do not leave in code +#define VPROF_TEST_SPIKE( msec ) CVProfSpikeDetector UNIQUE_ID( msec ) + +//----------------------------------------------------------------------------- + +#ifdef VPROF_VTUNE_GROUP +inline void CVProfile::PushGroup( int nGroupID ) +{ + // There is always at least one item on the stack since we force + // the first element to be VPROF_BUDGETGROUP_OTHER_UNACCOUNTED. + Assert( m_GroupIDStackDepth > 0 ); + Assert( m_GroupIDStackDepth < MAX_GROUP_STACK_DEPTH ); + m_GroupIDStack[m_GroupIDStackDepth] = nGroupID; + m_GroupIDStackDepth++; + if( m_GroupIDStack[m_GroupIDStackDepth-2] != nGroupID && + VTuneGroupEnabled() && + nGroupID == VTuneGroupID() ) + { + vtune( true ); + } +} +#endif // VPROF_VTUNE_GROUP + +#ifdef VPROF_VTUNE_GROUP +inline void CVProfile::PopGroup( void ) +{ + m_GroupIDStackDepth--; + // There is always at least one item on the stack since we force + // the first element to be VPROF_BUDGETGROUP_OTHER_UNACCOUNTED. + Assert( m_GroupIDStackDepth > 0 ); + if( m_GroupIDStack[m_GroupIDStackDepth] != m_GroupIDStack[m_GroupIDStackDepth+1] && + VTuneGroupEnabled() && + m_GroupIDStack[m_GroupIDStackDepth+1] == VTuneGroupID() ) + { + vtune( false ); + } +} +#endif // VPROF_VTUNE_GROUP + +//----------------------------------------------------------------------------- + +class CVProfScope +{ +public: + CVProfScope( const tchar * pszName, int detailLevel, const tchar *pBudgetGroupName, bool bAssertAccounted, int budgetFlags ); + ~CVProfScope(); + +private: + bool m_bEnabled; +}; + +//----------------------------------------------------------------------------- +// +// CVProfNode, inline methods +// + +inline CVProfNode::CVProfNode( const tchar * pszName, int detailLevel, CVProfNode *pParent, const tchar *pBudgetGroupName, int budgetFlags ) + : m_pszName( pszName ), + m_nCurFrameCalls( 0 ), + m_nPrevFrameCalls( 0 ), + m_nRecursions( 0 ), + m_pParent( pParent ), + m_pChild( NULL ), + m_pSibling( NULL ), + m_iClientData( -1 ) +#ifdef _X360 + , m_iBitFlags( 0 ) +#endif +{ + m_iUniqueNodeID = s_iCurrentUniqueNodeID++; + + if ( m_iUniqueNodeID > 0 ) + { + m_BudgetGroupID = g_VProfCurrentProfile.BudgetGroupNameToBudgetGroupID( pBudgetGroupName, budgetFlags ); + } + else + { + m_BudgetGroupID = 0; // "m_Root" can't call BudgetGroupNameToBudgetGroupID because g_VProfCurrentProfile not yet initialized + } + + Reset(); + + if( m_pParent && ( m_BudgetGroupID == VPROF_BUDGET_GROUP_ID_UNACCOUNTED ) ) + { + m_BudgetGroupID = m_pParent->GetBudgetGroupID(); + } +} + + +//------------------------------------- + +inline CVProfNode *CVProfNode::GetParent() +{ + Assert( m_pParent ); + return m_pParent; +} + +//------------------------------------- + +inline CVProfNode *CVProfNode::GetSibling() +{ + return m_pSibling; +} + +//------------------------------------- +// Hacky way to the previous sibling, only used from vprof panel at the moment, +// so it didn't seem like it was worth the memory waste to add the reverse +// link per node. + +inline CVProfNode *CVProfNode::GetPrevSibling() +{ + CVProfNode* p = GetParent(); + + if(!p) + return NULL; + + CVProfNode* s; + for( s = p->GetChild(); + s && ( s->GetSibling() != this ); + s = s->GetSibling() ) + ; + + return s; +} + +//------------------------------------- + +inline CVProfNode *CVProfNode::GetChild() +{ + return m_pChild; +} + +//------------------------------------- + +inline const tchar *CVProfNode::GetName() +{ + Assert( m_pszName ); + return m_pszName; +} + +//------------------------------------- + +inline int CVProfNode::GetTotalCalls() +{ + return m_nTotalCalls; +} + +//------------------------------------- + +inline double CVProfNode::GetTotalTime() +{ + return m_TotalTime.GetMillisecondsF(); +} + +//------------------------------------- + +inline int CVProfNode::GetCurCalls() +{ + return m_nCurFrameCalls; +} + +//------------------------------------- + +inline double CVProfNode::GetCurTime() +{ + return m_CurFrameTime.GetMillisecondsF(); +} + +//------------------------------------- + +inline int CVProfNode::GetPrevCalls() +{ + return m_nPrevFrameCalls; +} + +//------------------------------------- + +inline double CVProfNode::GetPrevTime() +{ + return m_PrevFrameTime.GetMillisecondsF(); +} + +//------------------------------------- + +inline double CVProfNode::GetPeakTime() +{ + return m_PeakTime.GetMillisecondsF(); +} + +//------------------------------------- + +inline double CVProfNode::GetTotalTimeLessChildren() +{ + double result = GetTotalTime(); + CVProfNode *pChild = GetChild(); + while ( pChild ) + { + result -= pChild->GetTotalTime(); + pChild = pChild->GetSibling(); + } + return result; +} + +//------------------------------------- + +inline double CVProfNode::GetCurTimeLessChildren() +{ + double result = GetCurTime(); + CVProfNode *pChild = GetChild(); + while ( pChild ) + { + result -= pChild->GetCurTime(); + pChild = pChild->GetSibling(); + } + return result; +} + +inline double CVProfNode::GetPrevTimeLessChildren() +{ + double result = GetPrevTime(); + CVProfNode *pChild = GetChild(); + while ( pChild ) + { + result -= pChild->GetPrevTime(); + pChild = pChild->GetSibling(); + } + return result; +} + +//----------------------------------------------------------------------------- +inline int CVProfNode::GetPrevL2CacheMissLessChildren() +{ + int result = m_iPrevL2CacheMiss; + CVProfNode *pChild = GetChild(); + while ( pChild ) + { + result -= pChild->m_iPrevL2CacheMiss; + pChild = pChild->GetSibling(); + } + return result; +} + +//----------------------------------------------------------------------------- +inline int CVProfNode::GetPrevLoadHitStoreLessChildren() +{ +#ifndef _X360 + return 0; +#else + int result = m_iPrevLoadHitStores; + CVProfNode *pChild = GetChild(); + while ( pChild ) + { + result -= pChild->m_iPrevLoadHitStores; + pChild = pChild->GetSibling(); + } + return result; +#endif +} + + +//----------------------------------------------------------------------------- +inline void CVProfNode::ClearPrevTime() +{ + m_PrevFrameTime.Init(); +} + +//----------------------------------------------------------------------------- +inline int CVProfNode::GetL2CacheMisses( void ) +{ +#ifndef _X360 + return m_L2Cache.GetL2CacheMisses(); +#else + return m_iTotalL2CacheMiss; +#endif +} + +#ifdef _X360 +inline int CVProfNode::GetLoadHitStores( void ) +{ + return m_iTotalLoadHitStores; +} +#endif + +//----------------------------------------------------------------------------- +// +// CVProfile, inline methods +// + +//------------------------------------- + +inline bool CVProfile::IsEnabled() const +{ + return ( m_enabled != 0 ); +} + +//------------------------------------- + +inline int CVProfile::GetDetailLevel() const +{ + return m_ProfileDetailLevel; +} + + +//------------------------------------- + +inline bool CVProfile::AtRoot() const +{ + return m_fAtRoot; +} + +//------------------------------------- + +inline void CVProfile::Start() +{ + if ( ++m_enabled == 1 ) + { + m_Root.EnterScope(); +#ifdef _X360 + VXProfileStart(); + CPMCData::InitializeOnceProgramWide(); +#endif + } +} + +//------------------------------------- + +inline void CVProfile::Stop() +{ + if ( --m_enabled == 0 ) + m_Root.ExitScope(); +} + +//------------------------------------- + +inline void CVProfile::EnterScope( const tchar *pszName, int detailLevel, const tchar *pBudgetGroupName, bool bAssertAccounted, int budgetFlags ) +{ + if ( ( m_enabled != 0 || !m_fAtRoot ) && InTargetThread() ) // if became disabled, need to unwind back to root before stopping + { + // Only account for vprof stuff on the primary thread. + //if( !Plat_IsPrimaryThread() ) + // return; + + if ( pszName != m_pCurNode->GetName() ) + { + m_pCurNode = m_pCurNode->GetSubNode( pszName, detailLevel, pBudgetGroupName, budgetFlags ); + } + m_pBudgetGroups[m_pCurNode->GetBudgetGroupID()].m_BudgetFlags |= budgetFlags; + +#if defined( _DEBUG ) && !defined( _X360 ) + // 360 doesn't want this to allow tier0 debug/release .def files to match + if ( bAssertAccounted ) + { + // FIXME + AssertOnce( m_pCurNode->GetBudgetGroupID() != 0 ); + } +#endif + m_pCurNode->EnterScope(); + m_fAtRoot = false; + } +#if defined(_X360) && defined(VPROF_PIX) + if ( m_pCurNode->GetBudgetGroupID() != VPROF_BUDGET_GROUP_ID_UNACCOUNTED ) + PIXBeginNamedEvent( 0, pszName ); +#endif +} + +inline void CVProfile::EnterScope( const tchar *pszName, int detailLevel, const tchar *pBudgetGroupName, bool bAssertAccounted ) +{ + EnterScope( pszName, detailLevel, pBudgetGroupName, bAssertAccounted, BUDGETFLAG_OTHER ); +} + +//------------------------------------- + +inline void CVProfile::ExitScope() +{ +#if defined(_X360) && defined(VPROF_PIX) +#ifdef PIXBeginNamedEvent +#error +#endif + if ( m_pCurNode->GetBudgetGroupID() != VPROF_BUDGET_GROUP_ID_UNACCOUNTED ) + PIXEndNamedEvent(); +#endif + if ( ( !m_fAtRoot || m_enabled != 0 ) && InTargetThread() ) + { + // Only account for vprof stuff on the primary thread. + //if( !Plat_IsPrimaryThread() ) + // return; + + // ExitScope will indicate whether we should back up to our parent (we may + // be profiling a recursive function) + if (m_pCurNode->ExitScope()) + { + m_pCurNode = m_pCurNode->GetParent(); + } + m_fAtRoot = ( m_pCurNode == &m_Root ); + } +} + +//------------------------------------- + +inline void CVProfile::Pause() +{ + m_pausedEnabledDepth = m_enabled; + m_enabled = 0; + if ( !AtRoot() ) + m_Root.Pause(); +} + +//------------------------------------- + +inline void CVProfile::Resume() +{ + m_enabled = m_pausedEnabledDepth; + if ( !AtRoot() ) + m_Root.Resume(); +} + +//------------------------------------- + +inline void CVProfile::Reset() +{ + m_Root.Reset(); + m_nFrames = 0; +} + +//------------------------------------- + +inline void CVProfile::ResetPeaks() +{ + m_Root.ResetPeak(); +} + +//------------------------------------- + +inline void CVProfile::MarkFrame() +{ + if ( m_enabled ) + { + ++m_nFrames; + m_Root.ExitScope(); + m_Root.MarkFrame(); + m_Root.EnterScope(); + +#ifdef _X360 + // update the CPU trace state machine if enabled + switch ( GetCPUTraceMode() ) + { + case kAllNodesInFrame_WaitingForMark: + // mark! Start recording a zillion traces. + m_iCPUTraceEnabled = kAllNodesInFrame_Recording; + break; + case kAllNodesInFrame_WaitingForMarkMultiFrame: + m_iCPUTraceEnabled = kAllNodesInFrame_RecordingMultiFrame; + break; + case kAllNodesInFrame_Recording: + // end of frame. stop recording if no more frames needed + m_iCPUTraceEnabled = kDisabled; + Msg("Frame ended. Recording no more CPU traces\n"); + + break; + case kAllNodesInFrame_RecordingMultiFrame: + // end of frame. stop recording if no more frames needed + if ( --m_nFramesRemaining == 0 ) + { + m_iCPUTraceEnabled = kDisabled; + Msg("Frames ended. Recording no more CPU traces\n"); + + SpewWorstMultiFrame(); + } + + ++m_nFrameCount; + + break; + default: + // no default + break; + } +#endif + } +} + +//------------------------------------- + +inline double CVProfile::GetTotalTimeSampled() +{ + return m_Root.GetTotalTime(); +} + +//------------------------------------- + +inline double CVProfile::GetPeakFrameTime() +{ + return m_Root.GetPeakTime(); +} + +//------------------------------------- + +inline double CVProfile::GetTimeLastFrame() +{ + return m_Root.GetCurTime(); +} + +//------------------------------------- + +inline CVProfNode *CVProfile::GetRoot() +{ + return &m_Root; +} + +//------------------------------------- + +inline CVProfNode *CVProfile::GetCurrentNode() +{ + return m_pCurNode; +} + + +inline const tchar *CVProfile::GetBudgetGroupName( int budgetGroupID ) +{ + Assert( budgetGroupID >= 0 && budgetGroupID < m_nBudgetGroupNames ); + return m_pBudgetGroups[budgetGroupID].m_pName; +} + +inline int CVProfile::GetBudgetGroupFlags( int budgetGroupID ) const +{ + Assert( budgetGroupID >= 0 && budgetGroupID < m_nBudgetGroupNames ); + return m_pBudgetGroups[budgetGroupID].m_BudgetFlags; +} + +#ifdef _X360 + +inline CVProfile::CPUTraceState CVProfile::GetCPUTraceMode() +{ + return m_iCPUTraceEnabled; +} + +inline void CVProfile::SetCPUTraceEnabled( CPUTraceState enabled, bool bTraceCompleteEvent /*=true*/, int nNumFrames /*= -1*/ ) +{ + m_iCPUTraceEnabled = enabled; + m_bTraceCompleteEvent = bTraceCompleteEvent; + if ( nNumFrames != -1 ) + { + m_nFramesRemaining = nNumFrames; + m_nFrameCount = 0; + m_WorstCycles = 0; + m_WorstTraceFilename[ 0 ] = 0; + } +} + +inline void CVProfile::IncrementMultiTraceIndex() +{ + ++m_iSuccessiveTraceIndex; +} + +inline unsigned int CVProfile::GetMultiTraceIndex() +{ + return m_iSuccessiveTraceIndex; +} + +#endif + + +//----------------------------------------------------------------------------- + +inline CVProfScope::CVProfScope( const tchar * pszName, int detailLevel, const tchar *pBudgetGroupName, bool bAssertAccounted, int budgetFlags ) + : m_bEnabled( g_VProfCurrentProfile.IsEnabled() ) +{ + if ( m_bEnabled ) + { + g_VProfCurrentProfile.EnterScope( pszName, detailLevel, pBudgetGroupName, bAssertAccounted, budgetFlags ); + } +} + +//------------------------------------- + +inline CVProfScope::~CVProfScope() +{ + if ( m_bEnabled ) + { + g_VProfCurrentProfile.ExitScope(); + } +} + +class CVProfCounter +{ +public: + CVProfCounter( const tchar *pName, CounterGroup_t group=COUNTER_GROUP_DEFAULT ) + { + m_pCounter = g_VProfCurrentProfile.FindOrCreateCounter( pName, group ); + Assert( m_pCounter ); + } + ~CVProfCounter() + { + } + void Increment( int val ) + { + Assert( m_pCounter ); + *m_pCounter += val; + } +private: + int *m_pCounter; +}; + +#endif + +#ifdef _X360 + +#include "xbox/xbox_console.h" +#include "tracerecording.h" +#include "tier1/fmtstr.h" +#pragma comment( lib, "tracerecording.lib" ) +#pragma comment( lib, "xbdm.lib" ) + +class CPIXRecorder +{ +public: + CPIXRecorder() : m_bActive( false ) {} + ~CPIXRecorder() { Stop(); } + + void Start( const char *pszFilename = "capture" ) + { + if ( !m_bActive ) + { + if ( !XTraceStartRecording( CFmtStr( "e:\\%s.pix2", pszFilename ) ) ) + { + Msg( "XTraceStartRecording failed, error code %d\n", GetLastError() ); + } + else + { + m_bActive = true; + } + } + } + + void Stop() + { + if ( m_bActive ) + { + m_bActive = false; + if ( XTraceStopRecording() ) + { + Msg( "CPU trace finished.\n" ); + // signal VXConsole that trace is completed + XBX_rTraceComplete(); + } + } + } + +private: + bool m_bActive; +}; + +#define VPROF_BEGIN_PIX_BLOCK( convar ) \ + { \ + bool bRunPix = 0; \ + static CFastTimer PIXTimer; \ + extern ConVar convar; \ + ConVar &PIXConvar = convar; \ + CPIXRecorder PIXRecorder; \ + { \ + PIXLabel: \ + if ( bRunPix ) \ + { \ + PIXRecorder.Start(); \ + } \ + else \ + { \ + if ( PIXConvar.GetBool() ) \ + { \ + PIXTimer.Start(); \ + } \ + } \ + { + + +#define VPROF_END_PIX_BLOCK() \ + } \ + \ + if ( !bRunPix ) \ + { \ + if ( PIXConvar.GetBool() ) \ + { \ + PIXTimer.End(); \ + if ( PIXTimer.GetDuration().GetMillisecondsF() > PIXConvar.GetFloat() ) \ + { \ + PIXConvar.SetValue( 0 ); \ + bRunPix = true; \ + goto PIXLabel; \ + } \ + } \ + } \ + else \ + { \ + PIXRecorder.Stop(); \ + } \ + } \ + } +#else +#define VPROF_BEGIN_PIX_BLOCK( PIXConvar ) { +#define VPROF_END_PIX_BLOCK() } +#endif + + +#ifdef VPROF_UNDO_PIX +#undef USE_PIX +#undef _PIX_H_ +#undef PIXBeginNamedEvent +#undef PIXEndNamedEvent +#undef PIXSetMarker +#undef PIXNameThread +#include <pix.h> +#endif + +#ifdef _MSC_VER +#pragma warning(pop) +#endif + +#endif + +//============================================================================= diff --git a/public/tier0/vprof_telemetry.h b/public/tier0/vprof_telemetry.h new file mode 100644 index 0000000..740f381 --- /dev/null +++ b/public/tier0/vprof_telemetry.h @@ -0,0 +1,154 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: Real-Time Hierarchical Telemetry Profiling +// +// $NoKeywords: $ +//=============================================================================// + +#ifndef VPROF_TELEMETRY_H +#define VPROF_TELEMETRY_H + +#if !defined( MAKE_VPC ) + +#if !defined( RAD_TELEMETRY_DISABLED ) && ( defined( IS_WINDOWS_PC ) || defined( _LINUX ) ) +// Rad Telemetry profiling is enabled on Win32 and Win64. +#define RAD_TELEMETRY_ENABLED +#endif + +#endif // !MAKE_VPC + + +#if !defined( RAD_TELEMETRY_ENABLED ) + +// +// Kill all tmZone() macros, etc. +// +#include "tmapi_dummy.h" + +inline void TelemetryTick() {} +inline void TelemetrySetLevel( unsigned int Level ) {} +#define TelemetrySetLockName( _ctx, _location, _description ) + +class CTelemetryLock +{ +public: + CTelemetryLock(void *plocation, const char *description) {} + ~CTelemetryLock() {} + void Locked() {} + void Unlocked() {} +}; + +class CTelemetrySpikeDetector +{ +public: + CTelemetrySpikeDetector( const char *msg, uint32 threshold = 50 ) {} + ~CTelemetrySpikeDetector() { } +}; + +#define TM_ZONE_DEFAULT( context ) +#define TM_ZONE_DEFAULT_PARAM( context, string_param ) + +#else + +// +// Telemetry is enabled. Include the telemetry header. +// +#include "../../thirdparty/telemetry/include/telemetry.h" +// Different versions of radbase.h define RADCOPYRIGHT to different values. So undef that here. +#undef RADCOPYRIGHT + +struct TelemetryData +{ + HTELEMETRY tmContext[32]; + float flRDTSCToMilliSeconds; // Conversion from tmFastTime() (rdtsc) to milliseconds. + uint32 FrameCount; // Count of frames to capture before turning off. + char ServerAddress[128]; // Server name to connect to. + int playbacktick; // GetPlaybackTick() value from demo file (or 0 if not playing a demo). + uint32 DemoTickStart; // Start telemetry on demo tick # + uint32 DemoTickEnd; // End telemetry on demo tick # + uint32 Level; // Current Telemetry level (Use TelemetrySetLevel to modify) +}; +PLATFORM_INTERFACE TelemetryData g_Telemetry; + +PLATFORM_INTERFACE void TelemetryTick(); +PLATFORM_INTERFACE void TelemetrySetLevel( unsigned int Level ); + +#define TELEMETRY_LEVEL0 g_Telemetry.tmContext[0] // high level tmZone() +#define TELEMETRY_LEVEL1 g_Telemetry.tmContext[1] // lower level tmZone(), tmZoneFiltered() +#define TELEMETRY_LEVEL2 g_Telemetry.tmContext[2] // VPROF_0 +#define TELEMETRY_LEVEL3 g_Telemetry.tmContext[3] // VPROF_1 +#define TELEMETRY_LEVEL4 g_Telemetry.tmContext[4] // VPROF_2 +#define TELEMETRY_LEVEL5 g_Telemetry.tmContext[5] // VPROF_3 +#define TELEMETRY_LEVEL6 g_Telemetry.tmContext[6] // VPROF_4 + +#define TM_ZONE_DEFAULT( context ) tmZone(context, TMZF_NONE, __FUNCTION__ ) +#define TM_ZONE_DEFAULT_PARAM( context, string_param ) tmZone(context, TMZF_NONE, "%s( %s )", __FUNCTION__ , tmDynamicString( context, (string_param) ) ) + +#define TelemetrySetLockName( _ctx, _location, _description ) \ + do \ + { \ + static bool s_bNameSet = false; \ + if( _ctx && !s_bNameSet ) \ + { \ + tmLockName( _ctx, _location, _description ); \ + s_bNameSet = true; \ + } \ + } while( 0 ) + +class CTelemetryLock +{ +public: + CTelemetryLock(void *plocation, const char *description) + { + m_plocation = (const char *)plocation; + m_description = description; + TelemetrySetLockName( TELEMETRY_LEVEL1, m_plocation, m_description ); + tmTryLock( TELEMETRY_LEVEL1, m_plocation, "%s", m_description ); + } + ~CTelemetryLock() + { + Unlocked(); + } + void Locked() + { + tmEndTryLock( TELEMETRY_LEVEL1, m_plocation, TMLR_SUCCESS ); + tmSetLockState( TELEMETRY_LEVEL1, m_plocation, TMLS_LOCKED, "%s Locked", m_description ); + } + void Unlocked() + { + if( m_plocation ) + { + tmSetLockState( TELEMETRY_LEVEL1, m_plocation, TMLS_RELEASED, "%s Released", m_description ); + m_plocation = NULL; + } + } + +public: + const char *m_plocation; + const char *m_description; +}; + +class CTelemetrySpikeDetector +{ +public: + // Spews Telemetry message when threshold hit (in milliseconds.) + CTelemetrySpikeDetector( const char *msg, float threshold = 5 ) : + m_message( msg ), m_threshold( threshold ), time0( tmFastTime() ) {} + ~CTelemetrySpikeDetector() + { + float time = ( tmFastTime() - time0 ) * g_Telemetry.flRDTSCToMilliSeconds; + if( time >= m_threshold ) + { + tmMessage( TELEMETRY_LEVEL0, TMMF_ICON_NOTE | TMMF_SEVERITY_WARNING, "(source/spike)%s %.2fms %t", m_message, time, tmSendCallStack( TELEMETRY_LEVEL0, 0 ) ); + } + } + +private: + TmU64 time0; + float m_threshold; + const char *m_message; +}; + +#endif // RAD_TELEMETRY_ENABLED + +#endif // VPROF_TELEMETRY_H diff --git a/public/tier0/wchartypes.h b/public/tier0/wchartypes.h new file mode 100644 index 0000000..814470f --- /dev/null +++ b/public/tier0/wchartypes.h @@ -0,0 +1,109 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: All of our code is completely Unicode. Instead of char, you should +// use wchar, uint8, or char8, as explained below. +// +// $NoKeywords: $ +//=============================================================================// + + +#ifndef WCHARTYPES_H +#define WCHARTYPES_H +#ifdef _WIN32 +#pragma once +#endif + +#ifdef _INC_TCHAR +#error ("Must include tier0 type headers before tchar.h") +#endif + +// Temporarily turn off Valve defines +#include "tier0/valve_off.h" + +#if !defined(_WCHAR_T_DEFINED) && !defined(GNUC) +typedef unsigned short wchar_t; +#define _WCHAR_T_DEFINED +#endif + +// char8 +// char8 is equivalent to char, and should be used when you really need a char +// (for example, when calling an external function that's declared to take +// chars). +typedef char char8; + +// uint8 +// uint8 is equivalent to byte (but is preferred over byte for clarity). Use this +// whenever you mean a byte (for example, one byte of a network packet). +typedef unsigned char uint8; +typedef unsigned char BYTE; +typedef unsigned char byte; + +// wchar +// wchar is a single character of text (currently 16 bits, as all of our text is +// Unicode). Use this whenever you mean a piece of text (for example, in a string). +typedef wchar_t wchar; +//typedef char wchar; + +// __WFILE__ +// This is a Unicode version of __FILE__ +#define WIDEN2(x) L ## x +#define WIDEN(x) WIDEN2(x) +#define __WFILE__ WIDEN(__FILE__) + +#ifdef STEAM +#ifndef _UNICODE +#define FORCED_UNICODE +#endif +#define _UNICODE +#endif + +#ifdef _WIN32 +#include <tchar.h> +#else +#define _tcsstr strstr +#define _tcsicmp stricmp +#define _tcscmp strcmp +#define _tcscpy strcpy +#define _tcsncpy strncpy +#define _tcsrchr strrchr +#define _tcslen strlen +#define _tfopen fopen +#define _stprintf sprintf +#define _ftprintf fprintf +#define _vsntprintf _vsnprintf +#define _tprintf printf +#define _sntprintf _snprintf +#define _T(s) s +#endif + +#if defined(_UNICODE) +typedef wchar tchar; +#define tstring wstring +#define __TFILE__ __WFILE__ +#define TCHAR_IS_WCHAR +#else +typedef char tchar; +#define tstring string +#define __TFILE__ __FILE__ +#define TCHAR_IS_CHAR +#endif + +#if defined( _MSC_VER ) || defined( WIN32 ) +typedef wchar_t uchar16; +typedef unsigned int uchar32; +#else +typedef unsigned short uchar16; +typedef wchar_t uchar32; +#endif + +#ifdef FORCED_UNICODE +#undef _UNICODE +#endif + +// Turn valve defines back on +#include "tier0/valve_on.h" + + +#endif // WCHARTYPES + + diff --git a/public/tier0/xbox_codeline_defines.h b/public/tier0/xbox_codeline_defines.h new file mode 100644 index 0000000..2e3b2c5 --- /dev/null +++ b/public/tier0/xbox_codeline_defines.h @@ -0,0 +1,16 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +//=============================================================================// + +#ifndef XBOX_CODELINE_DEFINES_H +#define XBOX_CODELINE_DEFINES_H + + +// In the regular src_main codeline, we leave this out. +//#define IN_XBOX_CODELINE + + +#endif // XBOX_CODELINE_DEFINES_H |