| # SPDX-License-Identifier: GPL-2.0 |
| # Copyright (c) 2019 Nikolay Borisov, SUSE LLC. All Rights Reserved. |
| # |
| # Parses btrfs' extent tree for holes. Holes are the ranges between 2 adjacent |
| # extent blocks. For example if we have the following 2 metadata items in the |
| # extent tree: |
| # item 6 key (30425088 METADATA_ITEM 0) itemoff 16019 itemsize 33 |
| # item 7 key (30490624 METADATA_ITEM 0) itemoff 15986 itemsize 33 |
| # |
| # There is a whole of 64k between then - 30490624−30425088 = 65536 |
| # Same logic applies for adjacent EXTENT_ITEMS. |
| # |
| # The script requires the following parameters passed on command line: |
| # * sectorsize - how many bytes per sector, used to convert the output of |
| # the script to sectors. |
| # * nodesize - size of metadata extents, used for internal calculations |
| |
| # Given an extent line "item 2 key (13672448 EXTENT_ITEM 65536) itemoff 16153 itemsize 53" |
| # or "item 6 key (30425088 METADATA_ITEM 0) itemoff 16019 itemsize 33" returns |
| # either 65536 (for data extents) or the fixes nodesize value for metadata |
| # extents. |
| function get_extent_size(line, tmp) { |
| if (line ~ data_match || line ~ bg_match) { |
| split(line, tmp) |
| gsub(/\)/,"", tmp[6]) |
| return tmp[6] |
| } else if (line ~ metadata_match) { |
| return nodesize |
| } |
| } |
| |
| # given a 'item 2 key (13672448 EXTENT_ITEM 65536) itemoff 16153 itemsize 53' |
| # and returns 13672448. |
| function get_extent_offset(line, tmp) { |
| split(line, tmp) |
| gsub(/\(/,"",tmp[4]) |
| return tmp[4] |
| } |
| |
| # This function parses all the extents belonging to a particular block group |
| # which are accumulated in lines[] and calculates the offsets of the holes |
| # part of this block group. |
| # |
| # base_offset and bg_line are local variables |
| function print_array(base_offset, bg_line) |
| { |
| if (match(lines[0], bg_match)) { |
| # we don't have an extent at the beginning of of blockgroup, so we |
| # have a hole between blockgroup offset and first extent offset |
| bg_line = lines[0] |
| prev_size=0 |
| prev_offset=get_extent_offset(bg_line) |
| delete lines[0] |
| } else { |
| # we have an extent at the beginning of block group, so initialize |
| # the prev_* vars correctly |
| bg_line = lines[1] |
| prev_size = get_extent_size(lines[0]) |
| prev_offset = get_extent_offset(lines[0]) |
| delete lines[1] |
| delete lines[0] |
| } |
| |
| bg_offset=get_extent_offset(bg_line) |
| bgend=bg_offset + get_extent_size(bg_line) |
| |
| for (i in lines) { |
| cur_size = get_extent_size(lines[i]) |
| cur_offset = get_extent_offset(lines[i]) |
| if (cur_offset != prev_offset + prev_size) |
| print int((prev_size + prev_offset) / sectorsize), int((cur_offset-1) / sectorsize) |
| prev_size = cur_size |
| prev_offset = cur_offset |
| } |
| |
| print int((prev_size + prev_offset) / sectorsize), int((bgend-1) / sectorsize) |
| total_printed++ |
| delete lines |
| } |
| |
| BEGIN { |
| loi_match="^.item [0-9]* key \\([0-9]* (BLOCK_GROUP_ITEM|METADATA_ITEM|EXTENT_ITEM) [0-9]*\\).*" |
| metadata_match="^.item [0-9]* key \\([0-9]* METADATA_ITEM [0-9]*\\).*" |
| data_match="^.item [0-9]* key \\([0-9]* EXTENT_ITEM [0-9]*\\).*" |
| bg_match="^.item [0-9]* key \\([0-9]* BLOCK_GROUP_ITEM [0-9]*\\).*" |
| node_match="^node.*$" |
| leaf_match="^leaf [0-9]* flags" |
| line_count=0 |
| total_printed=0 |
| skip_lines=0 |
| } |
| |
| { |
| # skip lines not belonging to a leaf |
| if (match($0, node_match)) { |
| skip_lines=1 |
| } else if (match($0, leaf_match)) { |
| skip_lines=0 |
| } |
| |
| if (!match($0, loi_match) || skip_lines == 1) next; |
| |
| # we have a line of interest, we need to parse it. First check if there is |
| # anything in the array |
| if (line_count==0) { |
| lines[line_count++]=$0; |
| } else { |
| prev_line=lines[line_count-1] |
| split(prev_line, prev_line_fields) |
| prev_objectid=prev_line_fields[4] |
| objectid=$4 |
| |
| if (objectid == prev_objectid && match($0, bg_match)) { |
| if (total_printed>0) { |
| # We are adding a BG after we have added its first extent |
| # previously, consider this a record ending event and just print |
| # the array |
| |
| delete lines[line_count-1] |
| print_array() |
| # we now start a new array with current and previous lines |
| line_count=0 |
| lines[line_count++]=prev_line |
| lines[line_count++]=$0 |
| } else { |
| # first 2 added lines are EXTENT and BG that match, in this case |
| # just add them |
| lines[line_count++]=$0 |
| |
| } |
| } else if (match($0, bg_match)) { |
| # ordinary end of record |
| print_array() |
| line_count=0 |
| lines[line_count++]=$0 |
| } else { |
| lines[line_count++]=$0 |
| } |
| } |
| } |
| |
| END { |
| print_array() |
| } |