Skip to content

ftrace logs parser focusing on f2fs/ext4 `address_space_operations` events for file block page-fault analysis.

Notifications You must be signed in to change notification settings

oslab-ewha/ftrace-aops-parser

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

13 Commits
 
 
 
 
 
 
 
 
 
 

Repository files navigation

ftrace for Android

테스트 환경

  • Model: Samsung Galaxy S22
  • Application Binary Interface (ABI): arm64-v8a

실행 방법

1. ADB (Android Debug Bridge) 설치

2. 이벤트 enable

[ADB Shell]

# enable tracing f2fs
for f in /sys/kernel/tracing/events/f2fs/*/enable; do echo 1 > "$f"; done
# enable tracing ext4
for f in /sys/kernel/tracing/events/ext4/*/enable; do echo 1 > "$f"; done
# enable tracing block I/O
for f in /sys/kernel/tracing/events/block/*/enable; do echo 1 > "$f"; done
# pid – ppid
echo 1 > /sys/kernel/tracing/events/sched/sched_process_fork/enable
# TGID (Thread Group ID)
echo 1 > /sys/kernel/tracing/options/record-tgid

3. 트레이스 추출

(A) 실시간으로 [수집 → PC 로컬 파일에 저장]

  1. [Bash / PowerShell] 시작
    adb shell "echo 1 > /sys/kernel/tracing/tracing_on"
    adb exec-out su -c "cat /sys/kernel/tracing/trace_pipe" > ftrace_live.txt
  2. [ADB Shell] 중지
    echo 0 > /sys/kernel/tracing/tracing_on
  3. [Bash / PowerShell] 조금 기다렸다가 Ctrl + C로 파일 스트리밍 중지

Note

/sys/kernel/tracing/tracing_on을 끄지 않은 상태로 [Bash / PowerShell] 에서 Ctrl + C로 중지하면 trace의 끝부분이 로그파일에 다 적히지 않은채로 중지되어 버리니 주의

(B) [ADB Shell] 에서 실행

* 해당 방식은 로그파일(/data/local/tmp/ftrace_live.txt)에 write 하는 것까지 전부 기록해서 로그파일 크기가 기하급수적으로 커지는 문제 발생. pid 지정 등 다른 방식을 함께 사용하는 것 고려하기

# 시작 (백그라운드)
echo 1 > /sys/kernel/tracing/tracing_on
cat /sys/kernel/tracing/trace_pipe > /data/local/tmp/ftrace_live.txt &

# 정지 (안전하게 멈추기)
echo 0 > /sys/kernel/tracing/tracing_on   # 먼저 기록 중단
pkill -f "cat /sys/kernel/tracing/trace_pipe"  # cat 프로세스 종료

# 파일 가져오기
adb pull /data/local/tmp/ftrace_live.txt .

4. 트레이스 파싱

python main.py -t ftrace_live.txt -o systrace_split.csv

5. inode로 파일 이름 찾기

(A) 여러 파일을 한꺼번에 찾을 때

  1. [Bash / PowerShell] inode 목록 파일을 Android 디바이스에 넣기
    adb push ./inodes.csv /data/local/tmp/ftrace-log/
  2. [ADB Shell] 다음과 같이 shell script 작성 후 실행
    INPUT_FILE='/data/local/tmp/ftrace-log/inodes.csv'
    DELIMITER=','
    OUTPUT_FILE='/data/local/tmp/ftrace-log/file_inode.csv'
    
    > "$OUTPUT_FILE"    # Leave blank before running
    
    for line in `cat $INPUT_FILE`
    do
        IFS=$DELIMITER read -r MAJ MIN INO <<< "$line"
        SU_CMD='MP=$(awk -v mm="'"${MAJ}:${MIN}"'" '"'"'$3==mm{print $5; exit}'"'"' /proc/self/mountinfo); su -c "find \"$MP\" -xdev -inum \"$INO\" -print 2>/dev/null"'
        FILENAME=$(eval "$SU_CMD" | head -n 1)
        if [[ $INO == "ino" ]]
        then
            FILENAME="filename"
        # 못 찾은 경우: /proc/*/fd 전체 스캔 (삭제된 파일도 표시됨)
        elif [ -z "$FILENAME" ]
        then
            FILENAME=$(su -c "ls -l /proc/*/fd/* 2>/dev/null | grep 'inode $INO ' | head -n 1 | awk '{print \$NF}'")
            if [ -z "$FILENAME" ]
            then
                FILENAME="NOT_FOUND"
            fi
        fi
    
        echo "$MAJ,$MIN,$INO,$FILENAME" >> "$OUTPUT_FILE"
    done
  3. [Bash / PowerShell] PC로 파일 빼내기
    adb pull /data/local/tmp/ftrace-log/file_inode.csv .

(B) [ADB Shell] 파일 하나만 찾을 때

예) dev_major: 254, dev_minor: 48, inode: 89120 일 때

MAJ=254; MIN=48; INO=89120
MP=$(awk -v mm="${MAJ}:${MIN}" '$3==mm{print $5; exit}' /proc/self/mountinfo) \
&& echo "mountpoint: $MP" \
&& su -c "find '$MP' -xdev -inum '$INO' -print 2>/dev/null"

6. file path로 파일 필터링

예) '.google.android.tts', '/system/fonts/' 를 제외한 다른 파일 접근은 필터링

python main.py -t ftrace_live.txt -o systrace_split.csv -i file_inode.csv -f .google.android.tts /system/fonts/

Notes

cf. Block IO Queueing Mechanism (blk-mq) (https://www.kernel.org/doc/html/next/block/blk-mq.html)

read/write/mmap(syscall)
        ↓
       VFS  ──(공통 인터페이스 호출: fops/aops 등)──→ ext4 또는 f2fs (구체 구현)
        ↓
     페이지 캐시 / 블록 레이어
        ↓
      저장장치
  • fops (file_operations) : file descriptor 레벨 진입점. open/llseek/read_iter/write_iter/mmap/fsync/ioctl/fallocate 등이 핵심
    • F2FS
      • File Read: f2fs_file_read_iter
      • File Write: f2fs_file_write_iter
    • ext4
      • File Read: ext4_file_read_iter
      • File Write: ext4_file_write_iter
  • aops (address_space_operations) : 페이지 캐시 레벨. readpage/write_start/write_end 같은 콜백이 핵심
  • iops(inode_operations) : 링크/디렉터리 조작 같은 메타데이터 중심
  • sops(super_operations) : 마운트/슈퍼블록 관리 (fops의 상위/동일 레벨)

2. Buffered I/O vs MMAP I/O

%%{init: {
  "theme": "base",
  "themeVariables": {
    "edgeLabelBackground": "#FFFFFF"
  }
}}%%
flowchart LR

%% 1) Row alignment links (put first so they are link 0,1,2)
BR --- BW --- MR --- MW

%% 2) Hide those three links
linkStyle 0 stroke-width:0px,stroke:transparent
linkStyle 1 stroke-width:0px,stroke:transparent
linkStyle 2 stroke-width:0px,stroke:transparent

classDef later stroke-dasharray: 5 3;

%% ========= Buffered Read =========
subgraph BR[Buffered Read]
  direction TB
  A0["**read(2)**"] --> A1["**fops.read_iter**"] --> A2{"Cache miss? <br/> (Page is not in cache?)"}
  A2 -- Yes --> A3["**aops.read_folio / aops.readpage(s)**"] --> A4["return from page cache"]
  A2 -- No  --> A4
end

%% ========= Buffered Write =========
subgraph BW[Buffered Write]
  direction TB
  B0["**write(2)**"] --> B1["**fops.write_iter**"] --> B2["**aops.write_begin**"]
  B2 --> B3["copy: user buffer -> page cache"] --> B4["**aops.write_end**"]
  B4 --> B5["**aops.dirty_folio / aops.set_page_dirty** <br/>to mark the page as dirty"] -.-> B6["later: **aops.writepage(s)** for writeback"]

class B6 later;
end

%% ========= mmap Read =========
subgraph MR[MMAP Read]
  direction TB
  C0["page fault"] --> C1["**vm_ops.fault** <br/>(ex: f2fs_filemap_fault)"] --> C2{"Cache miss?"}
  C2 -- Yes <br/> (major fault; load page) --> C3["**aops.read_folio / aops.readpage(s)**"] --> C4["access mapped page"]
  C2 -- No <br/> (minor fault)  --> C4
end

%% ========= mmap Write (MAP_SHARED & MAP_PRIVATE) =========
subgraph MW[MMAP Write]
  direction TB

  %% 공통 시작
  %% classDef empty width:0px,height:0px;
  classDef empty fill:none,stroke:none,color:transparent,width:0.5px,height:0.5px;

  D0["page fault"]
  D1["**vm_ops.fault**"]
  D2{"Cache miss?"}
  D3["**aops.read_folio / aops.readpage(s)**"]
  M1[ ]:::empty
  M2[ ]:::empty
  %%classDef empty fill:none,stroke:none;

  D0 --> D1 --> D2
  D2 -- Yes --> D3 --- M1
  D2 -- No --- M1
  M1 -- Access mapped page <br/>(to write data) --- M2
  M2 --> MWS
  M2 --> MWP

  %% MAP_SHARED 경로
  subgraph MWS[MAP_SHARED]
    direction TB
    S3["**vm_ops.page_mkwrite** (FS prepares space/journal/quota; may reserve blocks)"]
    S4["**aops.dirty_folio / aops.set_page_dirty** <br/>to mark the page as dirty"]
    S5["allow page to become dirty"]
    S6["later: **aops.writepage(s)** for writeback"]

    S3 --- S4 --> S5 -.-> S6
  end

  %% MAP_PRIVATE 경로
  subgraph MWP[MAP_PRIVATE]
    direction TB
    P3["Copy-On-Write: allocate anonymous page, copy contents"]
    P4["mark CoW page dirty (not file-backed)"]
    P5["write-back not needed (changes stay private)"]

    P3 --> P4 --> P5
  end
  class S6 later;
end


%% ========= Colors =========
classDef readNode fill:#E3F2FD,stroke:#1E88E5,color:#0D47A1,stroke-width:1px;
classDef writeNode fill:#E8F5E9,stroke:#2E7D32,color:#1B5E20,stroke-width:1px;
classDef mreadNode fill:#FFF8E1,stroke:#F9A825,color:#4E342E,stroke-width:1px;
classDef mwriteNode fill:#F3E5F5,stroke:#6A1B9A,color:#4A148C,stroke-width:1px;

class A0,A1,A2,A3,A4 readNode;
class B0,B1,B2,B3,B4,B5,B6 writeNode;
class C0,C1,C2,C3,C4 mreadNode;
class D0,D1,D2,S3,S4,S5,S6,P3,P4,P5 mwriteNode;

style A3 fill:#1E88E5,color:#E3F2FD,stroke-width:3px;
style B4 fill:#2E7D32,color:#E8F5E9,stroke-width:3px;
style C3 fill:#F9A825,color:#FFF8E1,stroke-width:3px;
style D3 fill:#6A1B9A,color:#F3E5F5,stroke-width:3px;

%% ========= (Optional) style subgraph boxes =========

style BR fill:#F0F7FF,stroke:#1E88E5,stroke-width:1px,color:#0D47A1
style BW fill:#F1F8E9,stroke:#2E7D32,stroke-width:1px,color:#1B5E20
style MR fill:#FFFDE7,stroke:#F9A825,stroke-width:1px,color:#4E342E
style MW fill:#F7F0FF,stroke:#6A1B9A,stroke-width:1px,color:#4A148C
style MWS fill:#F7F0FF,stroke:#6A1B9A,stroke-width:1px,color:#4A148C
style MWP fill:#F7F0FF,stroke:#6A1B9A,stroke-width:1px,color:#4A148C
Loading

https://lwn.net/Articles/357767/

When a process calls mmap(), the kernel sets up Virtual Memory Address (VMA) region to map the pages of the file to disk. It assigns the file's struct vm_operations to vma->vm_ops. struct vm_operations contains pointers to a set of functions which assist in getting the pages to memory on demand. vm_operations.fault() is called when the user access a virtual memory area not present in main memory. It is responsible for fetching the page from disk and putting it into memory. If the vma is shared, vm_operations.page_mkwrite() makes the page writable, otherwise the page is duplicated using COW(Copy On Write). page_mkwrite() is responsible for keeping track of all information required by the filesystem, such as buffer_heads, to put the data back on disk. Typically, this means preparing the block for write, checking that there is enough disk space (returning ENOSPC if not), and committing the write.

  • Trace files
    • trace (/sys/kernel/tracing/trace)
      • trace를 읽어도 버퍼가 비워지지 않음 (스냅샷 읽기)
      • 버퍼가 가득 차면 오래된 내용부터 덮어쓰기(overwrite)
      • 버퍼 크기는 /sys/kernel/tracing/buffer_size_kb로 설정
    • trace_pipe: /sys/kernel/tracing/trace_pipe
      • 읽으면서 소비(라이브 스트림)
      • "Once data is read from this file, it is consumed, and will not be read again with a sequential read"
  • Thread Group IDs (TGID)

    https://stackoverflow.com/questions/4517301/difference-between-pid-and-tid

    예) Binder:12758/12766 [001] ... → 12758 = TGID(프로세스), 12766 = TID(스레드)

    • record-tgid: "When any event or tracer is enabled, a hook is enabled in the sched_switch trace point to fill the cache of mapped Thread Group IDs (TGID) mapping to pids."
    • saved_tgids
  • Tracing with specific pids
    • set_ftrace_pid: "Have the function tracer only trace the threads whose PID are listed in this file."
    • function-fork: "When set, tasks with PIDs listed in set_ftrace_pid will have the PIDs of their children added to set_ftrace_pid when those tasks fork."

About

ftrace logs parser focusing on f2fs/ext4 `address_space_operations` events for file block page-fault analysis.

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Contributors 2

  •  
  •  

Languages