Coverage Report

Created: 2025-07-24 01:10

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/root/doris/be/src/util/disk_info.cpp
Line
Count
Source
1
// Licensed to the Apache Software Foundation (ASF) under one
2
// or more contributor license agreements.  See the NOTICE file
3
// distributed with this work for additional information
4
// regarding copyright ownership.  The ASF licenses this file
5
// to you under the Apache License, Version 2.0 (the
6
// "License"); you may not use this file except in compliance
7
// with the License.  You may obtain a copy of the License at
8
//
9
//   http://www.apache.org/licenses/LICENSE-2.0
10
//
11
// Unless required by applicable law or agreed to in writing,
12
// software distributed under the License is distributed on an
13
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
14
// KIND, either express or implied.  See the License for the
15
// specific language governing permissions and limitations
16
// under the License.
17
18
#include "util/disk_info.h"
19
20
// IWYU pragma: no_include <bthread/errno.h>
21
#include <absl/strings/str_split.h>
22
#include <errno.h> // IWYU pragma: keep
23
#include <stdio.h>
24
#include <stdlib.h>
25
#include <string.h>
26
#include <sys/stat.h>
27
#include <sys/sysmacros.h>
28
#include <sys/types.h>
29
30
#include <algorithm>
31
#include <boost/algorithm/string/classification.hpp>
32
#include <boost/algorithm/string/detail/classification.hpp>
33
#include <boost/algorithm/string/trim.hpp>
34
#include <fstream>
35
#include <iterator>
36
#include <memory>
37
#include <utility>
38
39
#include "common/cast_set.h"
40
#include "io/fs/local_file_system.h"
41
42
namespace doris {
43
44
#include "common/compile_check_begin.h"
45
46
bool DiskInfo::_s_initialized;
47
std::vector<DiskInfo::Disk> DiskInfo::_s_disks;
48
std::map<dev_t, int> DiskInfo::_s_device_id_to_disk_id;
49
std::map<std::string, int> DiskInfo::_s_disk_name_to_disk_id;
50
int DiskInfo::_s_num_datanode_dirs;
51
52
// Parses /proc/partitions to get the number of disks.  A bit of looking around
53
// seems to indicate this as the best way to do this.
54
// TODO: is there not something better than this?
55
1
void DiskInfo::get_device_names() {
56
    // Format of this file is:
57
    //    major, minor, #blocks, name
58
    // We are only interesting in name which is formatted as device_name<partition #>
59
    // The same device will show up multiple times for each partition (e.g. sda1, sda2).
60
1
    std::ifstream partitions("/proc/partitions", std::ios::in);
61
62
8
    while (partitions.good() && !partitions.eof()) {
63
7
        std::string line;
64
7
        getline(partitions, line);
65
7
        boost::trim(line);
66
67
7
        std::vector<std::string> fields = absl::StrSplit(line, " ", absl::SkipWhitespace());
68
69
7
        if (fields.size() != 4) {
70
2
            continue;
71
2
        }
72
73
5
        std::string name = fields[3];
74
75
5
        if (name == "name") {
76
1
            continue;
77
1
        }
78
79
        // Remove the partition# from the name.  e.g. sda2 --> sda
80
4
        boost::trim_right_if(name, boost::is_any_of("0123456789"));
81
82
        // Create a mapping of all device ids (one per partition) to the disk id.
83
4
        int major_dev_id = atoi(fields[0].c_str());
84
4
        int minor_dev_id = atoi(fields[1].c_str());
85
4
        dev_t dev = makedev(major_dev_id, minor_dev_id);
86
4
        DCHECK(_s_device_id_to_disk_id.find(dev) == _s_device_id_to_disk_id.end());
87
88
4
        int disk_id = -1;
89
4
        std::map<std::string, int>::iterator it = _s_disk_name_to_disk_id.find(name);
90
91
4
        if (it == _s_disk_name_to_disk_id.end()) {
92
            // First time seeing this disk
93
2
            disk_id = cast_set<int>(_s_disks.size());
94
2
            _s_disks.push_back(Disk(name, disk_id));
95
2
            _s_disk_name_to_disk_id[name] = disk_id;
96
2
        } else {
97
2
            disk_id = it->second;
98
2
        }
99
100
4
        _s_device_id_to_disk_id[dev] = disk_id;
101
4
    }
102
103
1
    if (partitions.is_open()) {
104
1
        partitions.close();
105
1
    }
106
107
1
    if (_s_disks.empty()) {
108
        // If all else fails, return 1
109
0
        LOG(WARNING) << "Could not determine number of disks on this machine.";
110
0
        _s_disks.push_back(Disk("sda", 0));
111
0
    }
112
113
    // Determine if the disk is rotational or not.
114
3
    for (int i = 0; i < _s_disks.size(); ++i) {
115
        // We can check if it is rotational by reading:
116
        // /sys/block/<device>/queue/rotational
117
        // If the file is missing or has unexpected data, default to rotational.
118
2
        std::stringstream ss;
119
2
        ss << "/sys/block/" << _s_disks[i].name << "/queue/rotational";
120
2
        std::ifstream rotational(ss.str().c_str(), std::ios::in);
121
2
        if (rotational.good()) {
122
2
            std::string line;
123
2
            getline(rotational, line);
124
2
            if (line == "0") {
125
0
                _s_disks[i].is_rotational = false;
126
0
            }
127
2
        }
128
2
        if (rotational.is_open()) {
129
2
            rotational.close();
130
2
        }
131
2
    }
132
1
}
133
134
1
void DiskInfo::init() {
135
1
    get_device_names();
136
1
    _s_initialized = true;
137
1
}
138
139
0
int DiskInfo::disk_id(const char* path) {
140
0
    struct stat s;
141
0
    stat(path, &s);
142
0
    std::map<dev_t, int>::iterator it = _s_device_id_to_disk_id.find(s.st_dev);
143
144
0
    if (it == _s_device_id_to_disk_id.end()) {
145
0
        return -1;
146
0
    }
147
148
0
    return it->second;
149
0
}
150
151
0
std::string DiskInfo::debug_string() {
152
0
    DCHECK(_s_initialized);
153
0
    std::stringstream stream;
154
0
    stream << "Disk Info: " << std::endl;
155
0
    stream << "  Num disks " << num_disks() << ": ";
156
157
0
    for (int i = 0; i < _s_disks.size(); ++i) {
158
0
        stream << _s_disks[i].name;
159
160
0
        if (i < num_disks() - 1) {
161
0
            stream << ", ";
162
0
        }
163
0
    }
164
165
0
    stream << std::endl;
166
0
    return stream.str();
167
0
}
168
169
Status DiskInfo::get_disk_devices(const std::vector<std::string>& paths,
170
0
                                  std::set<std::string>* devices) {
171
0
    std::vector<std::string> real_paths;
172
0
    for (auto& path : paths) {
173
0
        std::string p;
174
0
        Status st = io::global_local_filesystem()->canonicalize(path, &p);
175
0
        if (!st.ok()) {
176
0
            LOG(WARNING) << "skip disk monitoring of path. " << st;
177
0
            continue;
178
0
        }
179
0
        real_paths.emplace_back(std::move(p));
180
0
    }
181
182
0
    FILE* fp = fopen("/proc/mounts", "r");
183
0
    if (fp == nullptr) {
184
0
        std::stringstream ss;
185
0
        char buf[64];
186
0
        ss << "open /proc/mounts failed, errno:" << errno
187
0
           << ", message:" << strerror_r(errno, buf, 64);
188
0
        LOG(WARNING) << ss.str();
189
0
        return Status::InternalError(ss.str());
190
0
    }
191
192
0
    Status status;
193
0
    char* line_ptr = 0;
194
0
    size_t line_buf_size = 0;
195
0
    for (auto& path : real_paths) {
196
0
        size_t max_mount_size = 0;
197
0
        std::string match_dev;
198
0
        rewind(fp);
199
0
        while (getline(&line_ptr, &line_buf_size, fp) > 0) {
200
0
            char dev_path[4096];
201
0
            char mount_path[4096];
202
0
            int num = sscanf(line_ptr, "%4095s %4095s", dev_path, mount_path);
203
0
            if (num < 2) {
204
0
                continue;
205
0
            }
206
0
            size_t mount_size = strlen(mount_path);
207
0
            if (mount_size < max_mount_size || path.size() < mount_size ||
208
0
                strncmp(path.c_str(), mount_path, mount_size) != 0) {
209
0
                continue;
210
0
            }
211
0
            std::string dev(basename(dev_path));
212
0
            boost::trim_right_if(dev, boost::is_any_of("0123456789"));
213
0
            if (_s_disk_name_to_disk_id.find(dev) != std::end(_s_disk_name_to_disk_id)) {
214
0
                max_mount_size = mount_size;
215
0
                match_dev = dev;
216
0
            }
217
0
        }
218
0
        if (ferror(fp) != 0) {
219
0
            std::stringstream ss;
220
0
            char buf[64];
221
0
            ss << "open /proc/mounts failed, errno:" << errno
222
0
               << ", message:" << strerror_r(errno, buf, 64);
223
0
            LOG(WARNING) << ss.str();
224
0
            status = Status::InternalError(ss.str());
225
0
            break;
226
0
        }
227
0
        if (max_mount_size > 0) {
228
0
            devices->emplace(match_dev);
229
0
        }
230
0
    }
231
0
    if (line_ptr != nullptr) {
232
0
        free(line_ptr);
233
0
    }
234
0
    fclose(fp);
235
0
    return status;
236
0
}
237
238
#include "common/compile_check_end.h"
239
240
} // namespace doris