Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(195)

Side by Side Diff: chromeos/process_proxy/process_output_watcher.cc

Issue 261743002: Improve process output watcher's handling of multi-byte UTF8 characters (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: . Created 6 years, 7 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
OLDNEW
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 #include "chromeos/process_proxy/process_output_watcher.h" 5 #include "chromeos/process_proxy/process_output_watcher.h"
6 6
7 #include <sys/ioctl.h>
8 #include <sys/select.h>
9 #include <unistd.h>
10
7 #include <algorithm> 11 #include <algorithm>
8 #include <cstdio> 12 #include <cstdio>
9 #include <cstring> 13 #include <cstring>
10 14
11 #include <sys/ioctl.h>
12 #include <sys/select.h>
13 #include <unistd.h>
14
15 #include "base/logging.h" 15 #include "base/logging.h"
16 #include "base/posix/eintr_wrapper.h" 16 #include "base/posix/eintr_wrapper.h"
17 #include "base/third_party/icu/icu_utf.h"
17 18
18 namespace { 19 namespace {
19 20
20 void InitReadFdSet(int out_fd, int stop_fd, fd_set* set) { 21 void InitReadFdSet(int out_fd, int stop_fd, fd_set* set) {
21 FD_ZERO(set); 22 FD_ZERO(set);
22 if (out_fd != -1) 23 if (out_fd != -1)
23 FD_SET(out_fd, set); 24 FD_SET(out_fd, set);
24 FD_SET(stop_fd, set); 25 FD_SET(stop_fd, set);
25 } 26 }
26 27
27 void CloseFd(int* fd) { 28 void CloseFd(int* fd) {
28 if (*fd >= 0) { 29 if (*fd >= 0) {
29 if (IGNORE_EINTR(close(*fd)) != 0) 30 if (IGNORE_EINTR(close(*fd)) != 0)
30 DPLOG(WARNING) << "close fd " << *fd << " failed."; 31 DPLOG(WARNING) << "close fd " << *fd << " failed.";
31 } 32 }
32 *fd = -1; 33 *fd = -1;
33 } 34 }
34 35
36 // Gets byte size for a UTF8 character given it's leading byte. The character
37 // size is encoded as number of leading '1' bits in the character's leading
38 // byte. If the most significant bit is '0', the character is a valid ASCII
39 // and it's byte size is 1.
40 // The method returns 1 if the provided byte is invalid leading byte.
41 size_t UTF8SizeFromLeadingByte(uint8 leading_byte) {
42 size_t byte_count = 0;
43 uint8 mask = 1 << 7;
44 uint8 error_mask = 1 << (7 - CBU8_MAX_LENGTH);
45 while (leading_byte & mask) {
46 if (mask & error_mask)
47 return 1;
48 mask >>= 1;
49 ++byte_count;
50 }
51 return byte_count ? byte_count : 1;
52 }
53
35 } // namespace 54 } // namespace
36 55
37 namespace chromeos { 56 namespace chromeos {
38 57
39 ProcessOutputWatcher::ProcessOutputWatcher(int out_fd, int stop_fd, 58 ProcessOutputWatcher::ProcessOutputWatcher(
59 int out_fd,
60 int stop_fd,
40 const ProcessOutputCallback& callback) 61 const ProcessOutputCallback& callback)
41 : out_fd_(out_fd), 62 : read_buffer_size_(0),
63 out_fd_(out_fd),
42 stop_fd_(stop_fd), 64 stop_fd_(stop_fd),
43 on_read_callback_(callback) { 65 on_read_callback_(callback) {
44 VerifyFileDescriptor(out_fd_); 66 VerifyFileDescriptor(out_fd_);
45 VerifyFileDescriptor(stop_fd_); 67 VerifyFileDescriptor(stop_fd_);
46 max_fd_ = std::max(out_fd_, stop_fd_); 68 max_fd_ = std::max(out_fd_, stop_fd_);
47 // We want to be sure we will be able to add 0 at the end of the input, so -1. 69 // We want to be sure we will be able to add 0 at the end of the input, so -1.
48 read_buffer_size_ = arraysize(read_buffer_) - 1; 70 read_buffer_capacity_ = arraysize(read_buffer_) - 1;
49 } 71 }
50 72
51 void ProcessOutputWatcher::Start() { 73 void ProcessOutputWatcher::Start() {
52 WatchProcessOutput(); 74 WatchProcessOutput();
53 OnStop(); 75 OnStop();
54 } 76 }
55 77
56 ProcessOutputWatcher::~ProcessOutputWatcher() { 78 ProcessOutputWatcher::~ProcessOutputWatcher() {
57 CloseFd(&out_fd_); 79 CloseFd(&out_fd_);
58 CloseFd(&stop_fd_); 80 CloseFd(&stop_fd_);
59 } 81 }
60 82
61 void ProcessOutputWatcher::WatchProcessOutput() { 83 void ProcessOutputWatcher::WatchProcessOutput() {
62 while (true) { 84 while (true) {
63 // This has to be reset with every watch cycle. 85 // This has to be reset with every watch cycle.
64 fd_set rfds; 86 fd_set rfds;
65 DCHECK(stop_fd_ >= 0); 87 DCHECK_GE(stop_fd_, 0);
66 InitReadFdSet(out_fd_, stop_fd_, &rfds); 88 InitReadFdSet(out_fd_, stop_fd_, &rfds);
67 89
68 int select_result = 90 int select_result =
69 HANDLE_EINTR(select(max_fd_ + 1, &rfds, NULL, NULL, NULL)); 91 HANDLE_EINTR(select(max_fd_ + 1, &rfds, NULL, NULL, NULL));
70 92
71 if (select_result < 0) { 93 if (select_result < 0) {
72 DPLOG(WARNING) << "select failed"; 94 DPLOG(WARNING) << "select failed";
73 return; 95 return;
74 } 96 }
75 97
(...skipping 11 matching lines...) Expand all
87 void ProcessOutputWatcher::VerifyFileDescriptor(int fd) { 109 void ProcessOutputWatcher::VerifyFileDescriptor(int fd) {
88 CHECK_LE(0, fd); 110 CHECK_LE(0, fd);
89 CHECK_GT(FD_SETSIZE, fd); 111 CHECK_GT(FD_SETSIZE, fd);
90 } 112 }
91 113
92 void ProcessOutputWatcher::ReadFromFd(ProcessOutputType type, int* fd) { 114 void ProcessOutputWatcher::ReadFromFd(ProcessOutputType type, int* fd) {
93 // We don't want to necessary read pipe until it is empty so we don't starve 115 // We don't want to necessary read pipe until it is empty so we don't starve
94 // other streams in case data is written faster than we read it. If there is 116 // other streams in case data is written faster than we read it. If there is
95 // more than read_buffer_size_ bytes in pipe, it will be read in the next 117 // more than read_buffer_size_ bytes in pipe, it will be read in the next
96 // iteration. 118 // iteration.
97 ssize_t bytes_read = HANDLE_EINTR(read(*fd, read_buffer_, read_buffer_size_)); 119 DCHECK_GT(read_buffer_capacity_, read_buffer_size_);
120 ssize_t bytes_read =
121 HANDLE_EINTR(read(*fd,
122 &read_buffer_[read_buffer_size_],
123 read_buffer_capacity_ - read_buffer_size_));
98 if (bytes_read < 0) 124 if (bytes_read < 0)
99 DPLOG(WARNING) << "read from buffer failed"; 125 DPLOG(WARNING) << "read from buffer failed";
100 126
101 if (bytes_read > 0) { 127 if (bytes_read > 0)
102 on_read_callback_.Run(type, std::string(read_buffer_, bytes_read)); 128 ReportOutput(type, bytes_read);
103 }
104 129
105 // If there is nothing on the output the watched process has exited (slave end 130 // If there is nothing on the output the watched process has exited (slave end
106 // of pty is closed). 131 // of pty is closed).
107 if (bytes_read <= 0) { 132 if (bytes_read <= 0) {
108 // Slave pseudo terminal has been closed, we won't need master fd anymore. 133 // Slave pseudo terminal has been closed, we won't need master fd anymore.
109 CloseFd(fd); 134 CloseFd(fd);
110 135
111 // We have lost contact with the process, so report it. 136 // We have lost contact with the process, so report it.
112 on_read_callback_.Run(PROCESS_OUTPUT_TYPE_EXIT, ""); 137 on_read_callback_.Run(PROCESS_OUTPUT_TYPE_EXIT, "");
113 } 138 }
114 } 139 }
115 140
141 size_t ProcessOutputWatcher::OutputSizeWithoutIncompleteUTF8() {
142 // Find the last non-trailing character byte. This byte should be used to
143 // infer the last UTF8 character length.
144 int last_lead_byte = read_buffer_size_ - 1;
145 while (true) {
146 // If the series of trailing bytes is too long, something's not right.
147 // Report the whole output, without waiting for further character bytes.
148 if (read_buffer_size_ - last_lead_byte > CBU8_MAX_LENGTH)
149 return read_buffer_size_;
150
151 // If there are trailing characters, there must be a leading one in the
152 // buffer for a valid UTF8 character. Getting past the buffer begining
153 // signals something's wrong, or the buffer is empty. In both cases return
154 // the whole current buffer.
155 if (last_lead_byte < 0)
156 return read_buffer_size_;
157
158 // Found the starting character byte; stop searching.
159 if (!CBU8_IS_TRAIL(read_buffer_[last_lead_byte]))
160 break;
161
162 --last_lead_byte;
163 }
164
165 size_t last_length = UTF8SizeFromLeadingByte(read_buffer_[last_lead_byte]);
166
167 // Note that if |last_length| == 0 or
168 // |last_length| + |last_read_byte| < |read_buffer_size_|, the string is
169 // invalid UTF8. In that case, send the whole read buffer to the observer
170 // immediately, just as if there is no trailing incomplete UTF8 bytes.
171 if (!last_length || last_length + last_lead_byte <= read_buffer_size_)
172 return read_buffer_size_;
173
174 return last_lead_byte;
175 }
176
177 void ProcessOutputWatcher::ReportOutput(ProcessOutputType type,
178 size_t new_bytes_count) {
179 read_buffer_size_ += new_bytes_count;
180 size_t output_to_report = OutputSizeWithoutIncompleteUTF8();
181
182 on_read_callback_.Run(type, std::string(read_buffer_, output_to_report));
183
184 // Move the bytes that were left behind to the beginning of the buffer and
185 // update the buffer size accordingly.
186 if (output_to_report < read_buffer_size_) {
187 for (size_t i = output_to_report; i < read_buffer_size_; ++i) {
188 read_buffer_[i - output_to_report] = read_buffer_[i];
189 }
190 }
191 read_buffer_size_ -= output_to_report;
192 }
193
116 void ProcessOutputWatcher::OnStop() { 194 void ProcessOutputWatcher::OnStop() {
117 delete this; 195 delete this;
118 } 196 }
119 197
120 } // namespace chromeos 198 } // namespace chromeos
OLDNEW
« no previous file with comments | « chromeos/process_proxy/process_output_watcher.h ('k') | chromeos/process_proxy/process_output_watcher_unittest.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698