diff --git a/source/docs/data/memory_allocation_trace.csv b/source/docs/data/memory_allocation_trace.csv index 677d3f47fa..8290c455e7 100644 --- a/source/docs/data/memory_allocation_trace.csv +++ b/source/docs/data/memory_allocation_trace.csv @@ -1,7 +1,5 @@ "Kind","Operation","Agent_Id","Allocation_Size","Address","Correlation_Id","Start_Timestamp","End_Timestamp" -"MEMORY_ALLOCATION","MEMORY_ALLOCATION_ALLOCATE",Agent 0,1024,0x7fb2d0005000,11,3721742710532634,3721742710584854 -"MEMORY_ALLOCATION","MEMORY_ALLOCATION_FREE",Agent 0,0,0x7fb2d0005000,12,3721742710596404,3721742710933366 -"MEMORY_ALLOCATION","MEMORY_ALLOCATION_ALLOCATE",Agent 0,1024,0x7fb2d0005000,13,3721742710941416,3721742710960916 -"MEMORY_ALLOCATION","MEMORY_ALLOCATION_FREE",Agent 0,0,0x7fb2d0005000,14,3721742710967236,3721742711197647 -"MEMORY_ALLOCATION","MEMORY_ALLOCATION_ALLOCATE",Agent 0,1024,0x7fb2d0005000,15,3721742711204077,3721742711219717 -"MEMORY_ALLOCATION","MEMORY_ALLOCATION_FREE",Agent 0,0,0x7fb2d0005000,16,3721742711225857,3721742711466018 +"MEMORY_ALLOCATION","MEMORY_ALLOCATION_ALLOCATE","Agent 0",1024,"0x00007ffb26354000",1,816098791282238,816098791339655 +"MEMORY_ALLOCATION","MEMORY_ALLOCATION_ALLOCATE","Agent 0",1024,"0x00007ffb168d6000",2,816098791350331,816098791386746 +"MEMORY_ALLOCATION","MEMORY_ALLOCATION_FREE","",0,"0x00007ffb26354000",7,816098791533678,816098791678768 +"MEMORY_ALLOCATION","MEMORY_ALLOCATION_FREE","",0,"0x00007ffb168d6000",8,816098791681482,816098791873422 diff --git a/source/lib/output/generateCSV.cpp b/source/lib/output/generateCSV.cpp index aa848566c3..cccdfc834a 100644 --- a/source/lib/output/generateCSV.cpp +++ b/source/lib/output/generateCSV.cpp @@ -472,11 +472,11 @@ generate_csv(const output_config& {"Kind", "Operation", "Agent_Id", + "Allocation_Size", "Address", "Correlation_Id", "Start_Timestamp", - "End_Timestamp", - "Allocation_Size"}}; + "End_Timestamp"}}; for(auto ditr : data) { for(auto record : data.get(ditr)) @@ -498,11 +498,11 @@ generate_csv(const output_config& tool_metadata.get_kind_name(record.kind), api_name, agent_info, + record.allocation_size, rocprofiler::sdk::utility::as_hex(record.address.handle, 16), record.correlation_id.internal, record.start_timestamp, - record.end_timestamp, - record.allocation_size); + record.end_timestamp); ofs << row_ss.str(); } diff --git a/tests/hsa-memory-allocation/validate.py b/tests/hsa-memory-allocation/validate.py index 64faec960c..0b0fb0d70a 100644 --- a/tests/hsa-memory-allocation/validate.py +++ b/tests/hsa-memory-allocation/validate.py @@ -195,11 +195,16 @@ def test_memory_alloc_sizes(input_data): sdk_data = data["rocprofiler-sdk-json-tool"] # Op values: - # 0 == ??? (unknown) - # 1 == hsa_memory_allocate - # 2 == hsa_amd_vmem_handle_create - # 3 == hsa_memory_free - # 4 == hsa_amd_vmem_handle_release + UNKNOWN_OP = 0 + HSA_MEMORY_ALLOCATE_OP = 1 + HSA_AMD_VMEM_HANDLE_CREATE_OP = 2 + HSA_MEMORY_FREE_OP = 3 + HSA_AMD_VMEM_HANDLE_RELEASE = 4 + TOTAL_MEM_OPS = 5 + + ALLOCATE_OPS = (HSA_MEMORY_ALLOCATE_OP, HSA_AMD_VMEM_HANDLE_CREATE_OP) + FREE_OPS = (HSA_MEMORY_FREE_OP, HSA_AMD_VMEM_HANDLE_RELEASE) + memory_alloc_cnt = dict( [ (idx, {"agent": set(), "starting_addr": set(), "size": set(), "count": 0}) @@ -208,7 +213,7 @@ def test_memory_alloc_sizes(input_data): ) for itr in sdk_data["buffer_records"]["memory_allocations"]: op_id = itr["operation"] - assert op_id > 0 and op_id <= 5, f"{itr}" + assert op_id > UNKNOWN_OP and op_id < TOTAL_MEM_OPS, f"{itr}" memory_alloc_cnt[op_id]["count"] += 1 memory_alloc_cnt[op_id]["starting_addr"].add(itr.address) memory_alloc_cnt[op_id]["size"].add(itr.allocation_size) @@ -216,7 +221,7 @@ def test_memory_alloc_sizes(input_data): for itr in sdk_data["callback_records"]["memory_copies"]: op_id = itr.operation - assert op_id > 0 and op_id <= 5, f"{itr}" + assert op_id > UNKNOWN_OP and op_id < TOTAL_MEM_OPS, f"{itr}" memory_alloc_cnt[op_id]["count"] += 1 phase = itr.phase @@ -243,20 +248,26 @@ def test_memory_alloc_sizes(input_data): # 6 hsa_memory_allocation calls with 1024 bytes were called # and 9 hsa_amd_memory_pool_allocations with 2048 bytes # were called - assert memory_alloc_cnt[1]["count"] == 15 - assert memory_alloc_cnt[3]["count"] == 15 - # assert memory_alloc_cnt[3]["count"] == 3 - assert len(memory_alloc_cnt[1]["starting_addr"]) == len( - memory_alloc_cnt[3]["starting_addr"] - ) + assert memory_alloc_cnt[HSA_MEMORY_ALLOCATE_OP]["count"] == 15 + assert memory_alloc_cnt[HSA_MEMORY_FREE_OP]["count"] == 15 - # assert len(memory_alloc_cnt[3]["starting_addr"]) == 3 - assert len(memory_alloc_cnt[1]["size"]) == 2 - # assert len(memory_alloc_cnt[3]["size"]) == 1 - assert 1024 in memory_alloc_cnt[1]["size"] - assert 2048 in memory_alloc_cnt[1]["size"] - assert len(memory_alloc_cnt[1]["agent"]) == 2 - # assert len(memory_alloc_cnt[3]["agent"]) == 1 + # Check that allocation operations have corresponding free operations + assert len(memory_alloc_cnt[HSA_MEMORY_ALLOCATE_OP]["starting_addr"]) == len( + memory_alloc_cnt[HSA_MEMORY_FREE_OP]["starting_addr"] + ) + for starting_addr in memory_alloc_cnt[HSA_MEMORY_ALLOCATE_OP]["starting_addr"]: + assert starting_addr in memory_alloc_cnt[HSA_MEMORY_FREE_OP]["starting_addr"] + + # Confirm validation sizes are valid + assert len(memory_alloc_cnt[HSA_MEMORY_ALLOCATE_OP]["size"]) == 2 + assert ( + len(memory_alloc_cnt[HSA_MEMORY_FREE_OP]["size"]) == 1 + ) # size for free ops are 0 + assert 1024 in memory_alloc_cnt[HSA_MEMORY_ALLOCATE_OP]["size"] + assert 2048 in memory_alloc_cnt[HSA_MEMORY_ALLOCATE_OP]["size"] + + # Confirm that two agents were used + assert len(memory_alloc_cnt[HSA_MEMORY_ALLOCATE_OP]["agent"]) == 2 def test_retired_correlation_ids(input_data): diff --git a/tests/rocprofv3/memory-allocation/CMakeLists.txt b/tests/rocprofv3/memory-allocation/CMakeLists.txt index 368a35b1cd..2df58bc478 100644 --- a/tests/rocprofv3/memory-allocation/CMakeLists.txt +++ b/tests/rocprofv3/memory-allocation/CMakeLists.txt @@ -21,7 +21,7 @@ add_test( NAME rocprofv3-test-memory-allocation-tracing-execute COMMAND $ --memory-allocation-trace -d - ${CMAKE_CURRENT_BINARY_DIR}/%tag%-trace -o out --output-format json otf2 + ${CMAKE_CURRENT_BINARY_DIR}/%tag%-trace -o out --output-format json otf2 csv --log-level env -- $) set_tests_properties( @@ -36,7 +36,10 @@ add_test( ${Python3_EXECUTABLE} ${CMAKE_CURRENT_BINARY_DIR}/validate.py --json-input ${CMAKE_CURRENT_BINARY_DIR}/hsa-memory-allocation-trace/out_results.json --otf2-input - ${CMAKE_CURRENT_BINARY_DIR}/hsa-memory-allocation-trace/out_results.otf2) + ${CMAKE_CURRENT_BINARY_DIR}/hsa-memory-allocation-trace/out_results.otf2 + --csv-input + ${CMAKE_CURRENT_BINARY_DIR}/hsa-memory-allocation-trace/out_memory_allocation_trace.csv + ) set_tests_properties( rocprofv3-test-memory-allocation-tracing-validate diff --git a/tests/rocprofv3/memory-allocation/conftest.py b/tests/rocprofv3/memory-allocation/conftest.py index 6007ecc41d..bc395f9592 100644 --- a/tests/rocprofv3/memory-allocation/conftest.py +++ b/tests/rocprofv3/memory-allocation/conftest.py @@ -22,6 +22,7 @@ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN # THE SOFTWARE. +import csv import json import os import pytest @@ -43,7 +44,13 @@ def pytest_addoption(parser): "--otf2-input", action="store", default="memory-allocation-tracing/out_results.otf2", - help="Input JSON", + help="Input OTF2", + ) + parser.addoption( + "--csv-input", + action="store", + default="memory-allocation-tracing/out_memory_allocation_trace.csv", + help="Input CSV", ) @@ -60,3 +67,16 @@ def otf2_data(request): if not os.path.exists(filename): raise FileExistsError(f"{filename} does not exist") return OTF2Reader(filename).read()[0] + + +@pytest.fixture +def csv_data(request): + filename = request.config.getoption("--csv-input") + data = [] + if not os.path.isfile(filename): + raise FileExistsError(f"{filename} does not exist") + with open(filename, "r") as inp: + reader = csv.DictReader(inp) + for row in reader: + data.append(row) + return data diff --git a/tests/rocprofv3/memory-allocation/validate.py b/tests/rocprofv3/memory-allocation/validate.py index f3cb03cb64..00ce677c3d 100755 --- a/tests/rocprofv3/memory-allocation/validate.py +++ b/tests/rocprofv3/memory-allocation/validate.py @@ -65,12 +65,23 @@ def test_memory_allocation(json_data): HSA_AMD_VMEM_HANDLE_CREATE_OP = 2 HSA_MEMORY_FREE_OP = 3 HSA_AMD_VMEM_HANDLE_RELEASE = 4 + TOTAL_MEM_OPS = 5 + + ALLOCATE_OPS = (HSA_MEMORY_ALLOCATE_OP, HSA_AMD_VMEM_HANDLE_CREATE_OP) + FREE_OPS = (HSA_MEMORY_FREE_OP, HSA_AMD_VMEM_HANDLE_RELEASE) valid_agent_ids = set() for row in data["agents"]: if "id" in row and "handle" in row.id: valid_agent_ids.add(row.id.handle) + memory_alloc_cnt = dict( + [ + (idx, {"agent": set(), "starting_addr": set(), "size": set(), "count": 0}) + for idx in range(1, TOTAL_MEM_OPS) + ] + ) + # check buffering data for node in memory_allocation_data: assert "size" in node @@ -86,13 +97,20 @@ def test_memory_allocation(json_data): assert "allocation_size" in node assert node.size > 0 - assert node.allocation_size >= 0 assert len(node.address) > 0 assert node.thread_id > 0 + # Check that op ID is valid op_id = node.operation - assert op_id != UNKNOWN_OP - if op_id == HSA_MEMORY_ALLOCATE_OP or op_id == HSA_AMD_VMEM_HANDLE_CREATE_OP: + assert op_id > UNKNOWN_OP and op_id < TOTAL_MEM_OPS + # Summarize info in dict + memory_alloc_cnt[op_id]["count"] += 1 + memory_alloc_cnt[op_id]["starting_addr"].add(node.address) + memory_alloc_cnt[op_id]["size"].add(node.allocation_size) + memory_alloc_cnt[op_id]["agent"].add(node.agent_id.handle) + + # Check if agent is valid + if op_id in ALLOCATE_OPS: assert node.agent_id.handle in valid_agent_ids else: assert node.agent_id.handle == 0 # free ops record agent id as null @@ -107,6 +125,31 @@ def test_memory_allocation(json_data): in bf_op_names ) + # In the memory allocation test which generates this file + # 6 hsa_memory_allocation calls with 1024 bytes were called + # and 9 hsa_amd_memory_pool_allocations with 2048 bytes + # were called + assert memory_alloc_cnt[HSA_MEMORY_ALLOCATE_OP]["count"] == 15 + assert memory_alloc_cnt[HSA_MEMORY_FREE_OP]["count"] == 15 + + # Check that allocation operations have corresponding free operations + assert len(memory_alloc_cnt[HSA_MEMORY_ALLOCATE_OP]["starting_addr"]) == len( + memory_alloc_cnt[HSA_MEMORY_FREE_OP]["starting_addr"] + ) + for starting_addr in memory_alloc_cnt[HSA_MEMORY_ALLOCATE_OP]["starting_addr"]: + assert starting_addr in memory_alloc_cnt[HSA_MEMORY_FREE_OP]["starting_addr"] + + # Confirm validation sizes are valid + assert len(memory_alloc_cnt[HSA_MEMORY_ALLOCATE_OP]["size"]) == 2 + assert ( + len(memory_alloc_cnt[HSA_MEMORY_FREE_OP]["size"]) == 1 + ) # size for free ops are 0 + assert 1024 in memory_alloc_cnt[HSA_MEMORY_ALLOCATE_OP]["size"] + assert 2048 in memory_alloc_cnt[HSA_MEMORY_ALLOCATE_OP]["size"] + + # Confirm that two agents were used + assert len(memory_alloc_cnt[HSA_MEMORY_ALLOCATE_OP]["agent"]) == 2 + def test_otf2_data(otf2_data, json_data): import rocprofiler_sdk.tests.rocprofv3 as rocprofv3 @@ -114,6 +157,97 @@ def test_otf2_data(otf2_data, json_data): rocprofv3.test_otf2_data(otf2_data, json_data, ("memory_allocation",)) +def test_csv_data(csv_data): + assert len(csv_data) > 0, "Expected non-empty csv data" + + ALLOCATION_OPS = ( + "MEMORY_ALLOCATION_ALLOCATE", + "ROCPROFILER_MEMORY_ALLOCATION_VMEM_ALLOCATE", + ) + FREE_OPS = ( + "ROCPROFILER_MEMORY_ALLOCATION_FREE", + "ROCPROFILER_MEMORY_ALLOCATION_VMEM_FREE", + ) + + memory_allocation_info = dict( + { + "agents": set(), + "allocation_size": defaultdict(int), + "address": defaultdict(lambda: defaultdict(int)), + } + ) + + for row in csv_data: + assert ( + "Kind" in row + ), "'Kind' was not present in csv data for memory-allocation-trace" + assert ( + "Operation" in row + ), "'Operation' was not present in csv data for memory-allocation-trace" + assert ( + "Agent_Id" in row + ), "'Agent_Id' was not present in csv data for memory-allocation-trace" + assert ( + "Allocation_Size" in row + ), "'Allocation_Size' was not present in csv data for memory-allocation-trace" + assert ( + "Address" in row + ), "'Address' was not present in csv data for memory-allocation-trace" + assert ( + "Correlation_Id" in row + ), "'Correlation_Id' was not present in csv data for memory-allocation-trace" + assert ( + "Start_Timestamp" in row + ), "'Start_Timestamp' was not present in csv data for memory-allocation-trace" + assert ( + "End_Timestamp" in row + ), "'End_Timestamp' was not present in csv data for memory-allocation-trace" + + assert row["Kind"] == "MEMORY_ALLOCATION" + assert row["Operation"] in ( + "MEMORY_ALLOCATION_ALLOCATE", + "MEMORY_ALLOCATION_FREE", + ) + assert int(row["Correlation_Id"]) > 0 + + # Check if agent ID is assigned to correct row + if row["Operation"] in ALLOCATION_OPS: + assert row["Agent_Id"].split(" ")[0] == "Agent" + memory_allocation_info["agents"].add(row["Agent_Id"]) + else: + assert row["Agent_Id"] == "" # free ops have no agent + + # Confirm allocation size is valid + if row["Operation"] in ALLOCATION_OPS: + assert int(row["Allocation_Size"]) in ( + 1024, + 2048, + ) # Test allocates 1024 and 2048 bytes only + memory_allocation_info["allocation_size"][int(row["Allocation_Size"])] += 1 + else: + row["Allocation_Size"] == 0 # Free ops record allocation size as 0 + + # Confirm address is valid + assert row["Address"][:2] == "0x" + address = int(row["Address"], 16) + if row["Operation"] in ALLOCATION_OPS: + memory_allocation_info["address"][address]["allocation"] += 1 + else: + memory_allocation_info["address"][address]["free"] += 1 + # Timestamp sanity check + assert int(row["Start_Timestamp"]) > 0 + assert int(row["End_Timestamp"]) > 0 + assert int(row["Start_Timestamp"]) < int(row["End_Timestamp"]) + # Test uses 2 agents with 6 allocations of 1024 bytes and 9 allocations of 2048 bytes + assert len(memory_allocation_info["agents"]) == 2 + assert memory_allocation_info["allocation_size"][1024] == 6 + assert memory_allocation_info["allocation_size"][2048] == 9 + for address in memory_allocation_info["address"].values(): + assert ( + address["allocation"] == address["free"] + ) # Free should have corresponding allocate + + if __name__ == "__main__": exit_code = pytest.main(["-x", __file__] + sys.argv[1:]) sys.exit(exit_code)