1 // Copyright 2017 The Crashpad Authors. All rights reserved.
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 // http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14
15 #include "handler/mac/file_limit_annotation.h"
16
17 #include <dirent.h>
18 #include <errno.h>
19 #include <string.h>
20 #include <sys/resource.h>
21 #include <sys/sysctl.h>
22 #include <sys/types.h>
23
24 #include <string>
25
26 #include "base/format_macros.h"
27 #include "base/stl_util.h"
28 #include "base/strings/stringprintf.h"
29 #include "client/crashpad_info.h"
30 #include "client/simple_string_dictionary.h"
31 #include "util/posix/scoped_dir.h"
32
33 namespace crashpad {
34
35 namespace {
36
37 // rv is the return value from sysctl() or sysctlbyname(), and value and size
38 // are the pointers passed as oldp and oldlenp. If sysctl() failed, the returned
39 // string will be "E" followed by the error number. If there was a size
40 // mismatch, the returned string will be "Z" followed by the size indicated by
41 // sysctl(). Normally, a string representation of *value will be returned.
FormatFromSysctl(int rv,const int * value,const size_t * size)42 std::string FormatFromSysctl(int rv, const int* value, const size_t* size) {
43 if (rv != 0) {
44 return base::StringPrintf("E%d", errno);
45 }
46 if (*size != sizeof(*value)) {
47 return base::StringPrintf("Z%zu", *size);
48 }
49 return base::StringPrintf("%d", *value);
50 }
51
52 // Counts the number of open file descriptors in the process and returns it as a
53 // string. This /dev/fd and the value returned will include the open file
54 // descriptor for that directory. If opendir() fails, the returned string will
55 // be "E" followed by the error number. If readdir() fails, it will be "R"
56 // followed by the error number.
CountOpenFileDescriptors()57 std::string CountOpenFileDescriptors() {
58 DIR* dir = opendir("/dev/fd");
59 if (!dir) {
60 return base::StringPrintf("E%d", errno);
61 }
62
63 ScopedDIR dir_owner(dir);
64
65 dirent* entry;
66 int count = 0;
67 while ((errno = 0, entry = readdir(dir)) != nullptr) {
68 const char* entry_name = entry->d_name;
69 if (strcmp(entry_name, ".") == 0 || strcmp(entry_name, "..") == 0) {
70 continue;
71 }
72
73 ++count;
74 }
75
76 if (errno != 0) {
77 return base::StringPrintf("R%d", errno);
78 }
79
80 return base::StringPrintf("%d", count);
81 }
82
83 // Returns a string for |limit|, or "inf" if |limit| is RLIM_INFINITY.
StringForRLim(rlim_t limit)84 std::string StringForRLim(rlim_t limit) {
85 if (limit == RLIM_INFINITY) {
86 return std::string("inf");
87 }
88
89 return base::StringPrintf("%" PRIu64, limit);
90 }
91
92 } // namespace
93
RecordFileLimitAnnotation()94 void RecordFileLimitAnnotation() {
95 CrashpadInfo* crashpad_info = CrashpadInfo::GetCrashpadInfo();
96 SimpleStringDictionary* simple_annotations =
97 crashpad_info->simple_annotations();
98 if (!simple_annotations) {
99 simple_annotations = new SimpleStringDictionary();
100 crashpad_info->set_simple_annotations(simple_annotations);
101 }
102
103 int value;
104 size_t size = sizeof(value);
105 std::string num_files = FormatFromSysctl(
106 sysctlbyname("kern.num_files", &value, &size, nullptr, 0), &value, &size);
107
108 int mib[] = {CTL_KERN, KERN_MAXFILES};
109 size = sizeof(value);
110 std::string max_files = FormatFromSysctl(
111 sysctl(mib, base::size(mib), &value, &size, nullptr, 0), &value, &size);
112
113 std::string open_files = CountOpenFileDescriptors();
114
115 rlimit limit;
116 std::string nofile;
117 if (getrlimit(RLIMIT_NOFILE, &limit) != 0) {
118 nofile = base::StringPrintf("E%d,E%d", errno, errno);
119 } else {
120 nofile =
121 StringForRLim(limit.rlim_cur) + "," + StringForRLim(limit.rlim_max);
122 }
123
124 std::string annotation = base::StringPrintf("%s,%s,%s,%s",
125 num_files.c_str(),
126 max_files.c_str(),
127 open_files.c_str(),
128 nofile.c_str());
129 simple_annotations->SetKeyValue("file-limits", annotation.c_str());
130 }
131
132 } // namespace crashpad
133