#include #include #include #include #include #include #include #include #include #define MALI_DEVICE "/dev/mali0" #define KBASE_IOCTL_TYPE 0x80 #define KBASE_IOCTL_VERSION_CHECK \ _IOWR(KBASE_IOCTL_TYPE, 0, struct kbase_ioctl_version_check) #define KBASE_IOCTL_SET_FLAGS \ _IOW(KBASE_IOCTL_TYPE, 1, struct kbase_ioctl_set_flags) #define KBASE_IOCTL_TLSTREAM_ACQUIRE \ _IOW(KBASE_IOCTL_TYPE, 18, struct kbase_ioctl_tlstream_acquire) #define BASE_CONTEXT_CREATE_FLAG_NONE 0 #define BASE_CONTEXT_CREATE_FLAG_MONITOR (1 << 4) #define BASE_KERNEL_UKERNEL_VERSION(a, b) (((a) << 16) + (b)) struct kbase_ioctl_version_check { __u16 major; __u16 minor; }; struct kbase_ioctl_set_flags { __u32 create_flags; }; struct kbase_ioctl_tlstream_acquire { __u32 flags; }; #define PACKET_HEADER_SIZE 8 #define PACKET_SIZE 4096 #define PACKET_TYPE_SUMMARY 2 #define PACKET_CLASS_OBJ 0 enum tl_msg_id { KBASE_TL_NEW_CTX = 0, KBASE_TL_NEW_GPU = 1, KBASE_TL_NEW_LPU = 2, KBASE_TL_NEW_ATOM = 3, KBASE_TL_NEW_AS = 4, KBASE_TL_DEL_CTX = 5, KBASE_TL_DEL_ATOM = 6, KBASE_TL_LIFELINK_LPU_GPU = 7, KBASE_TL_LIFELINK_AS_GPU = 8, KBASE_TL_RET_CTX_LPU = 9, KBASE_TL_RET_ATOM_CTX = 10, KBASE_TL_RET_ATOM_LPU = 11, KBASE_TL_NRET_CTX_LPU = 12, KBASE_TL_NRET_ATOM_CTX = 13, KBASE_TL_NRET_ATOM_LPU = 14, KBASE_TL_RET_AS_CTX = 15, KBASE_TL_NRET_AS_CTX = 16, KBASE_TL_RET_ATOM_AS = 17, KBASE_TL_NRET_ATOM_AS = 18, KBASE_TL_DEP_ATOM_ATOM = 19, KBASE_TL_NDEP_ATOM_ATOM = 20, KBASE_TL_RDEP_ATOM_ATOM = 21, KBASE_TL_ATTRIB_ATOM_CONFIG = 22, KBASE_TL_ATTRIB_ATOM_PRIORITY = 23, KBASE_TL_ATTRIB_ATOM_STATE = 24, KBASE_TL_ATTRIB_ATOM_PRIORITIZED = 25, KBASE_TL_ATTRIB_ATOM_JIT = 26, KBASE_TL_ATTRIB_AS_CONFIG = 27, KBASE_TL_EVENT_LPU_SOFTSTOP = 28, KBASE_TL_EVENT_ATOM_SOFTSTOP_EX = 29, KBASE_TL_EVENT_ATOM_SOFTSTOP_ISSUE = 30, KBASE_JD_GPU_SOFT_RESET = 31, }; static void *ptr_from_payload(const uint8_t *data, int offset) { void *ptr; memcpy(&ptr, data + offset, sizeof(ptr)); return ptr; } static uint32_t u32_from_payload(const uint8_t *data, int offset) { uint32_t v; memcpy(&v, data + offset, sizeof(v)); return v; } static uint64_t u64_from_payload(const uint8_t *data, int offset) { uint64_t v; memcpy(&v, data + offset, sizeof(v)); return v; } struct fmt_spec { const char *types; int payload_size; }; static struct fmt_spec get_format(int msg_id) { switch (msg_id) { case KBASE_TL_NEW_CTX: return (struct fmt_spec){"pII", 8+4+4}; case KBASE_TL_NEW_GPU: return (struct fmt_spec){"pII", 8+4+4}; case KBASE_TL_NEW_LPU: return (struct fmt_spec){"pII", 8+4+4}; case KBASE_TL_NEW_ATOM: return (struct fmt_spec){"pI", 8+4}; case KBASE_TL_NEW_AS: return (struct fmt_spec){"pI", 8+4}; case KBASE_TL_DEL_CTX: return (struct fmt_spec){"p", 8}; case KBASE_TL_DEL_ATOM: return (struct fmt_spec){"p", 8}; case KBASE_TL_LIFELINK_LPU_GPU: return (struct fmt_spec){"pp", 8+8}; case KBASE_TL_LIFELINK_AS_GPU: return (struct fmt_spec){"pp", 8+8}; case KBASE_TL_RET_CTX_LPU: return (struct fmt_spec){"pp", 8+8}; case KBASE_TL_RET_ATOM_CTX: return (struct fmt_spec){"pp", 8+8}; case KBASE_TL_RET_ATOM_LPU: return (struct fmt_spec){"pp", 8+8+4}; case KBASE_TL_NRET_CTX_LPU: return (struct fmt_spec){"pp", 8+8}; case KBASE_TL_NRET_ATOM_CTX: return (struct fmt_spec){"pp", 8+8}; case KBASE_TL_NRET_ATOM_LPU: return (struct fmt_spec){"pp", 8+8}; case KBASE_TL_RET_AS_CTX: return (struct fmt_spec){"pp", 8+8}; case KBASE_TL_NRET_AS_CTX: return (struct fmt_spec){"pp", 8+8}; case KBASE_TL_RET_ATOM_AS: return (struct fmt_spec){"pp", 8+8}; case KBASE_TL_NRET_ATOM_AS: return (struct fmt_spec){"pp", 8+8}; case KBASE_TL_DEP_ATOM_ATOM: return (struct fmt_spec){"pp", 8+8}; case KBASE_TL_NDEP_ATOM_ATOM: return (struct fmt_spec){"pp", 8+8}; case KBASE_TL_RDEP_ATOM_ATOM: return (struct fmt_spec){"pp", 8+8}; case KBASE_TL_ATTRIB_ATOM_CONFIG: return (struct fmt_spec){"pLLI", 8+8+8+4}; case KBASE_TL_ATTRIB_ATOM_PRIORITY: return (struct fmt_spec){"pI", 8+4}; case KBASE_TL_ATTRIB_ATOM_STATE: return (struct fmt_spec){"pI", 8+4}; case KBASE_TL_ATTRIB_ATOM_PRIORITIZED: return (struct fmt_spec){"p", 8}; case KBASE_TL_ATTRIB_ATOM_JIT: return (struct fmt_spec){"pLL", 8+8+8}; case KBASE_TL_ATTRIB_AS_CONFIG: return (struct fmt_spec){"pLLL", 8+8+8+8}; case KBASE_TL_EVENT_LPU_SOFTSTOP: return (struct fmt_spec){"p", 8}; case KBASE_TL_EVENT_ATOM_SOFTSTOP_EX: return (struct fmt_spec){"p", 8}; case KBASE_TL_EVENT_ATOM_SOFTSTOP_ISSUE: return (struct fmt_spec){"p", 8}; case KBASE_JD_GPU_SOFT_RESET: return (struct fmt_spec){"p", 8}; default: return (struct fmt_spec){NULL, 0}; } } static const char *msg_name(int msg_id) { switch (msg_id) { case KBASE_TL_NEW_CTX: return "NEW_CTX"; case KBASE_TL_NEW_GPU: return "NEW_GPU"; case KBASE_TL_NEW_LPU: return "NEW_LPU"; case KBASE_TL_NEW_ATOM: return "NEW_ATOM"; case KBASE_TL_NEW_AS: return "NEW_AS"; case KBASE_TL_ATTRIB_AS_CONFIG: return "ATTRIB_AS_CONFIG"; case KBASE_TL_LIFELINK_LPU_GPU: return "LIFELINK_LPU_GPU"; case KBASE_TL_LIFELINK_AS_GPU: return "LIFELINK_AS_GPU"; default: return "?"; } } static void parse_timeline_packet(const uint8_t *buf, int len) { int off = PACKET_HEADER_SIZE; uint32_t word0, word1; memcpy(&word0, buf, 4); memcpy(&word1, buf+4, 4); int numbered = (word1 >> 24) & 1; if (numbered) off += 4; while (off + 4 + 8 <= len) { uint32_t msg_id; memcpy(&msg_id, buf + off, 4); off += 4; uint64_t ts; memcpy(&ts, buf + off, 8); off += 8; struct fmt_spec fmt = get_format(msg_id); if (fmt.types == NULL) { break; } if (off + fmt.payload_size > len) break; int nptrs = 0; for (const char *p = fmt.types; *p; p++) if (*p == 'p') nptrs++; printf(" [%s] (ts=%llu, %d bytes payload)", msg_name(msg_id), (unsigned long long)ts, fmt.payload_size); int poff = 0; int ptr_idx = 0; for (const char *p = fmt.types; *p; p++) { if (*p == 'p') { uint64_t ptr; memcpy(&ptr, buf + off + poff, sizeof(uint64_t)); int is_kernel = (ptr >> 32) == 0xffffff80 || (ptr >> 32) == 0xffffffc0; printf(" ptr%d=0x%lx%s", ptr_idx, (unsigned long)ptr, is_kernel ? " *** KERNEL" : ""); ptr_idx++; poff += 8; } else if (*p == 'I' || *p == 'L') { int sz = (*p == 'I') ? 4 : 8; if (sz == 4) { uint32_t v; memcpy(&v, buf + off + poff, 4); printf(" val%d=%u", ptr_idx, v); } else { uint64_t v; memcpy(&v, buf + off + poff, 8); printf(" val%d=%llu", ptr_idx, (unsigned long long)v); } poff += sz; } } printf("\n"); off += fmt.payload_size; } } int main() { int fd = open(MALI_DEVICE, O_RDWR); if (fd < 0) { perror("open"); return 1; } struct kbase_ioctl_version_check ver = {.major = 1, .minor = 0}; if (ioctl(fd, KBASE_IOCTL_VERSION_CHECK, &ver)) { perror("VERSION_CHECK"); close(fd); return 1; } printf("Version: major=%u minor=%u\n", ver.major, ver.minor); struct kbase_ioctl_set_flags sf = {.create_flags = BASE_CONTEXT_CREATE_FLAG_NONE}; if (ioctl(fd, KBASE_IOCTL_SET_FLAGS, &sf)) { perror("SET_FLAGS"); close(fd); return 1; } printf("SET_FLAGS ok\n"); struct kbase_ioctl_tlstream_acquire ta = {.flags = 0}; int tlfd = ioctl(fd, KBASE_IOCTL_TLSTREAM_ACQUIRE, &ta); if (tlfd < 0) { perror("TLSTREAM_ACQUIRE"); close(fd); return 1; } printf("TLSTREAM_ACQUIRE ok, tlfd=%d\n", tlfd); sleep(2); uint8_t buf[PACKET_SIZE * 4]; int total = 0; for (int i = 0; i < 8; i++) { int n = read(tlfd, buf + total, PACKET_SIZE); if (n <= 0) { if (n < 0) perror("read"); break; } total += n; printf("Read %d bytes from tlstream (total=%d)\n", n, total); if (n < PACKET_SIZE) break; } printf("\nRaw hex dump (%d bytes):\n", total); for (int i = 0; i < total && i < 512; i++) { if (i % 16 == 0) printf("\n%04x: ", i); printf("%02x ", buf[i]); } printf("\n"); printf("\nParsing packets:\n"); int off = 0; while (off + PACKET_HEADER_SIZE <= total) { uint32_t word0, word1; memcpy(&word0, buf+off, 4); memcpy(&word1, buf+off+4, 4); int pkt_family = (word0 >> 26) & 0x3f; int pkt_class = (word0 >> 19) & 0x7f; int pkt_type = (word0 >> 16) & 0x7; int stream_id = word0 & 0xff; int data_length = word1 & 0xffffff; int numbered = (word1 >> 24) & 1; int pkt_total = PACKET_HEADER_SIZE + (numbered ? 4 : 0) + data_length; if (off + pkt_total > total) { printf("Packet %d: incomplete (%d+%d > %d)\n", off, off, pkt_total, total); break; } static const char *family_str[] = {"CTRL", "TL"}; static const char *class_str[] = {"OBJ", "AUX"}; static const char *type_str[] = {"HEADER", "BODY", "SUMMARY"}; printf("\nPacket at offset %d: family=%s class=%s type=%s stream=%d len=%d numbered=%d\n", off, family_str[pkt_family], class_str[pkt_class], type_str[pkt_type], stream_id, data_length, numbered); if (pkt_type == PACKET_TYPE_SUMMARY && pkt_class == PACKET_CLASS_OBJ) { parse_timeline_packet(buf + off, pkt_total); } off += pkt_total; } close(tlfd); close(fd); return 0; }