Skip to content

Commit 87ce671

Browse files
committed
init
0 parents  commit 87ce671

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

65 files changed

+145450
-0
lines changed

browser_adapters/chromium.py

Lines changed: 252 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,252 @@
1+
import os
2+
import random
3+
import logging
4+
import signal
5+
import psutil
6+
import copy
7+
from common import FuzzedBrowser
8+
from selenium.webdriver.common.utils import free_port
9+
from selenium.common.exceptions import WebDriverException
10+
from selenium.common.exceptions import TimeoutException
11+
from selenium.webdriver import Chrome, ChromeOptions
12+
from selenium.common.exceptions import UnexpectedAlertPresentException
13+
14+
from typing import List, Tuple, Optional
15+
import subprocess
16+
17+
18+
class ChromiumSeleniumBrowser(FuzzedBrowser):
19+
def __init__(self, thread_id, timeout):
20+
self.thread_id = thread_id
21+
self.timeout_sec = int(timeout) / 1000
22+
self.browser = None # actually it is a driver
23+
self.tmp_dir = "/tmp/chromiumtmpdir" + str(self.thread_id) + "pid" + str(
24+
os.getpid()) + "rand" + str(random.random())
25+
os.makedirs(self.tmp_dir, exist_ok=True)
26+
self.msg_path = self.tmp_dir + "/tmp_log"
27+
self.use_xvfb = True
28+
if "NO_XVFB" in os.environ:
29+
logging.info(f"[{self.thread_id}]: no xvfb")
30+
self.use_xvfb = False
31+
32+
if self.use_xvfb:
33+
self.display_port = free_port()
34+
logging.info(f"[{self.thread_id}]: display port: {self.display_port}")
35+
self.xvfb = subprocess.Popen(
36+
["Xvfb", f":{self.display_port}", "-ac", "-maxclients", "2048"])
37+
else:
38+
self.display_port = None
39+
self.xvfb = None
40+
41+
# close the browser with this probability
42+
self.close_browser_prob = 0.01
43+
if "CLOSE_BROWSER_PROB" in os.environ:
44+
self.close_browser_prob = float(os.environ["CLOSE_BROWSER_PROB"])
45+
46+
if "CHROMIUM_PATH" in os.environ:
47+
self.chromium_path = os.environ["CHROMIUM_PATH"]
48+
else:
49+
logging.error(f"[{thread_id}]: didn't set CHROMIUM_PATH env var")
50+
exit(1)
51+
if "CHROMEDRIVER_PATH" in os.environ:
52+
self.chrome_driver_path = os.environ["CHROMEDRIVER_PATH"]
53+
else:
54+
logging.error(f"[{thread_id}]: didn't set CHROMEDRIVER_PATH env var")
55+
exit(1)
56+
os.environ["LD_LIBRARY_PATH"] = "/".join(self.chromium_path.split("/")[:-1])
57+
logging.info(f"[{self.thread_id}]: LD_LIBRARY_PATH: {os.environ['LD_LIBRARY_PATH']}")
58+
self.termination_log = None
59+
60+
def __del__(self):
61+
try:
62+
self.xvfb.kill()
63+
except:
64+
pass
65+
try:
66+
self.close_browser()
67+
except:
68+
pass
69+
70+
def launch_browser(self):
71+
logging.info(f"[{self.thread_id}]: start launching")
72+
if self.use_xvfb and self.xvfb.poll() is not None:
73+
logging.info(f"[{self.thread_id}]: xvfb has been kill. port: {self.display_port}")
74+
self.xvfb.kill()
75+
self.xvfb = subprocess.Popen(
76+
["Xvfb", f":{self.display_port}", "-ac", "-maxclients", "2048"])
77+
78+
# file_output = open(self.msg_path, 'w')
79+
# logging.info(f"[{self.thread_id}]: free port {port}")
80+
# self.chromium = subprocess.Popen(
81+
# [self.chromium_path, "--no-zygote", "--no-sandbox",
82+
# "--remote-debugging-port=" + port],
83+
# stdout=file_output, stderr=file_output)
84+
#
85+
ops = ChromeOptions()
86+
ops.binary_location = self.chromium_path
87+
# port = str(free_port())
88+
# ops.debugger_address = "127.0.0.1:" + port
89+
ops.add_argument("--no-sandbox")
90+
ops.add_argument("--disable-setuid-sandbox")
91+
ops.add_argument("--no-zygote")
92+
ops.add_argument("--disable-dev-shm-usage" ) # https://stackoverflow.com/questions/50642308/webdriverexception-unknown-error-devtoolsactiveport-file-doesnt-exist-while-t
93+
try:
94+
if self.use_xvfb:
95+
# it's very very dangerous, but I can't find a better way
96+
# because selenium didn't export an API for setting env var
97+
os.environ["DISPLAY"] = f":{self.display_port}"
98+
self.browser = Chrome(self.chrome_driver_path, options=ops
99+
, service_args=["--verbose", f"--log-path={self.msg_path}"])
100+
self.browser.set_page_load_timeout(self.timeout_sec)
101+
self.browser.command_executor.set_timeout(self.timeout_sec * 10)
102+
logging.info(f"[{self.thread_id}]: end launching")
103+
chromium_pid = self.browser.service.process.pid
104+
logging.info(f"[{self.thread_id}]: browser pid: {chromium_pid}")
105+
except UnexpectedAlertPresentException as e:
106+
self.browser.switch_to.alert.accept()
107+
self.new_page()
108+
except BaseException as e:
109+
logging.error(f"[{self.thread_id}]: cannot launch browser. {repr(e)}")
110+
# raise
111+
logging.error(f"[{self.thread_id}]: try again")
112+
self.launch_browser()
113+
114+
def close_browser(self):
115+
driver_pid = self.browser.service.process.pid
116+
process = psutil.Process(driver_pid)
117+
child_procs = process.children(recursive=True)
118+
try:
119+
self.browser.quit()
120+
logging.info(f"[{self.thread_id}]: successfully quit")
121+
except BaseException as e:
122+
logging.info(f"[{self.thread_id}]: cannot normally quit. cause: {repr(e)}")
123+
os.kill(driver_pid, signal.SIGKILL)
124+
for pid in child_procs:
125+
logging.info(f"[{self.thread_id}]: try to kill pid: {pid.pid}")
126+
try:
127+
os.kill(pid.pid, signal.SIGKILL)
128+
logging.info(f"[{self.thread_id}]: successfully kill pid {pid.pid}")
129+
except ProcessLookupError as e:
130+
pass
131+
except BaseException as e:
132+
logging.info(f"[{self.thread_id}]: cannot kill {pid.pid}; cause: {repr(e)}")
133+
self.browser = None
134+
135+
def close_all_tabs(self):
136+
try:
137+
handles = self.browser.window_handles
138+
if len(handles) == 1:
139+
return
140+
handle_0 = handles[0]
141+
for handle in handles:
142+
if handle_0 != handle:
143+
self.browser.switch_to.window(handle)
144+
self.browser.close()
145+
self.browser.switch_to.window(handle_0)
146+
except BaseException as e:
147+
raise
148+
149+
def new_page(self):
150+
try:
151+
self.close_all_tabs()
152+
self.browser.switch_to.window(self.browser.window_handles[0])
153+
self.browser.execute_script("window.open('','_blank');")
154+
self.browser.switch_to.window(self.browser.window_handles[1])
155+
except BaseException as e:
156+
logging.error(
157+
f"[{self.thread_id}]: cannot create a new page, try to restart the browser. reason: {repr(e)}")
158+
# logging.error(
159+
# f"[{self.thread_id}]: {self.message()}"
160+
# )
161+
self.close_browser()
162+
self.launch_browser()
163+
self.new_page()
164+
165+
def ready(self):
166+
if self.browser is None:
167+
self.launch_browser()
168+
self.new_page()
169+
else:
170+
try:
171+
self.browser.switch_to.alert.accept()
172+
except BaseException as e:
173+
pass
174+
try:
175+
r = random.random()
176+
if r < self.close_browser_prob:
177+
logging.info(
178+
f"restart browser because the random pick: {r} {self.close_browser_prob}")
179+
self.close_browser()
180+
self.launch_browser()
181+
self.new_page()
182+
except BaseException as e:
183+
logging.error(f"[{self.thread_id}]: cannot new a page. {repr(e)}")
184+
self.close_browser()
185+
self.launch_browser()
186+
self.new_page()
187+
188+
def clone(self):
189+
cloned = copy.copy(self)
190+
cloned.browser = None
191+
return cloned
192+
193+
def fuzz(self, path: str) -> bool:
194+
path = "file://" + path
195+
try:
196+
self.browser.get(path)
197+
return True
198+
except UnexpectedAlertPresentException as e:
199+
logging.info(f"[{self.thread_id}]: {repr(e)}!")
200+
self.browser.switch_to.alert.accept()
201+
return True
202+
except TimeoutException as e:
203+
logging.info(f"[{self.thread_id}]: timeout, {repr(e)}!")
204+
try:
205+
self.browser.close()
206+
except BaseException as e:
207+
logging.info(
208+
f"[{self.thread_id}]: timeout, but cannot close current window. {repr(e)}")
209+
self.close_browser()
210+
self.launch_browser()
211+
return True
212+
except BaseException as e:
213+
try:
214+
logging.info(f"[{self.thread_id}]: not finish, because: {repr(e)}")
215+
self.browser.close()
216+
logging.info(f"[{self.thread_id}]: browser can be closed!")
217+
return True
218+
except WebDriverException as e:
219+
logging.info(f"[{self.thread_id}]: crash! WebDriverException: {repr(e)}")
220+
self.termination_log = self.get_log()
221+
self.close_browser()
222+
self.launch_browser()
223+
return False
224+
except BaseException as e:
225+
logging.info(f"[{self.thread_id}]: cannot close: {repr(e)}")
226+
self.close_browser()
227+
self.launch_browser()
228+
return True
229+
230+
def message(self) -> str:
231+
if self.termination_log:
232+
tmp = self.termination_log
233+
self.termination_log = None
234+
return tmp
235+
else:
236+
return self.get_log()
237+
238+
def get_log(self) -> str:
239+
try:
240+
with open(self.msg_path, "r") as f:
241+
return f.read()
242+
except UnicodeDecodeError as e:
243+
return "fail to decode current file"
244+
except IOError as e:
245+
return f"[{self.thread_id}]: fail to open msg_path. {repr(e)}"
246+
247+
def get_statement_valid_feedback(self) -> Optional[str]:
248+
try:
249+
feedback = self.browser.execute_script("return JSON.stringify(myFeedback)")
250+
return feedback
251+
except BaseException as e:
252+
return None

0 commit comments

Comments
 (0)