summaryrefslogtreecommitdiff
path: root/src/parse-extent-tree.awk
blob: 1e69693c3d54a07858f6cd188d0a556f28471aa7 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
# 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()
}