PageRenderTime 44ms CodeModel.GetById 21ms RepoModel.GetById 0ms app.codeStats 0ms

/src/compiler/package.cpp

https://github.com/github-ivan/hiphop-php
C++ | 418 lines | 351 code | 49 blank | 18 comment | 71 complexity | 8dab94daa7a6a228e7b0e0ff80486e19 MD5 | raw file
  1. /*
  2. +----------------------------------------------------------------------+
  3. | HipHop for PHP |
  4. +----------------------------------------------------------------------+
  5. | Copyright (c) 2010- Facebook, Inc. (http://www.facebook.com) |
  6. +----------------------------------------------------------------------+
  7. | This source file is subject to version 3.01 of the PHP license, |
  8. | that is bundled with this package in the file LICENSE, and is |
  9. | available through the world-wide-web at the following url: |
  10. | http://www.php.net/license/3_01.txt |
  11. | If you did not receive a copy of the PHP license and are unable to |
  12. | obtain it through the world-wide-web, please send a note to |
  13. | license@php.net so we can mail you a copy immediately. |
  14. +----------------------------------------------------------------------+
  15. */
  16. #include <compiler/package.h>
  17. #include <sys/types.h>
  18. #include <sys/stat.h>
  19. #include <unistd.h>
  20. #include <dirent.h>
  21. #include <compiler/analysis/analysis_result.h>
  22. #include <compiler/parser/parser.h>
  23. #include <compiler/analysis/symbol_table.h>
  24. #include <compiler/analysis/variable_table.h>
  25. #include <compiler/option.h>
  26. #include <util/process.h>
  27. #include <util/util.h>
  28. #include <util/logger.h>
  29. #include <util/json.h>
  30. #include <util/db_conn.h>
  31. #include <util/db_query.h>
  32. #include <util/exception.h>
  33. #include <util/preprocess.h>
  34. #include <util/job_queue.h>
  35. #include <runtime/base/execution_context.h>
  36. using namespace HPHP;
  37. using namespace std;
  38. ///////////////////////////////////////////////////////////////////////////////
  39. Package::Package(const char *root, bool bShortTags /* = true */,
  40. bool bAspTags /* = false */)
  41. : m_bShortTags(bShortTags), m_bAspTags(bAspTags), m_files(4000),
  42. m_dispatcher(0), m_lineCount(0), m_charCount(0) {
  43. m_root = Util::normalizeDir(root);
  44. m_ar = AnalysisResultPtr(new AnalysisResult());
  45. m_fileCache = FileCachePtr(new FileCache());
  46. }
  47. void Package::addAllFiles(bool force) {
  48. if (Option::PackageDirectories.empty() && Option::PackageFiles.empty()) {
  49. addDirectory("/", force);
  50. } else {
  51. for (set<string>::const_iterator iter = Option::PackageDirectories.begin();
  52. iter != Option::PackageDirectories.end(); ++iter) {
  53. addDirectory(*iter, force);
  54. }
  55. for (set<string>::const_iterator iter = Option::PackageFiles.begin();
  56. iter != Option::PackageFiles.end(); ++iter) {
  57. addSourceFile((*iter).c_str());
  58. }
  59. }
  60. }
  61. void Package::addInputList(const char *listFileName) {
  62. ASSERT(listFileName && *listFileName);
  63. FILE *f = fopen(listFileName, "r");
  64. if (f == NULL) {
  65. throw Exception("Unable to open %s: %s", listFileName,
  66. Util::safe_strerror(errno).c_str());
  67. }
  68. char fileName[PATH_MAX];
  69. while (fgets(fileName, sizeof(fileName), f)) {
  70. int len = strlen(fileName);
  71. if (fileName[len - 1] == '\n') fileName[len - 1] = '\0';
  72. len = strlen(fileName);
  73. if (len) {
  74. if (fileName[len - 1] == '/') {
  75. addDirectory(fileName, false);
  76. } else {
  77. addSourceFile(fileName);
  78. }
  79. }
  80. }
  81. fclose(f);
  82. }
  83. void Package::addStaticFile(const char *fileName) {
  84. ASSERT(fileName && *fileName);
  85. m_extraStaticFiles.insert(fileName);
  86. }
  87. void Package::addStaticDirectory(const std::string path) {
  88. m_staticDirectories.insert(path);
  89. }
  90. void Package::addDirectory(const std::string &path, bool force) {
  91. addDirectory(path.c_str(), force);
  92. }
  93. void Package::addDirectory(const char *path, bool force) {
  94. m_directories.insert(path);
  95. addPHPDirectory(path, force);
  96. }
  97. void Package::addPHPDirectory(const char *path, bool force) {
  98. vector<string> files;
  99. if (force) {
  100. Util::find(files, m_root, path, true);
  101. } else {
  102. Util::find(files, m_root, path, true,
  103. &Option::PackageExcludeDirs, &Option::PackageExcludeFiles);
  104. Option::FilterFiles(files, Option::PackageExcludePatterns);
  105. }
  106. int rootSize = m_root.size();
  107. for (unsigned int i = 0; i < files.size(); i++) {
  108. const string &file = files[i];
  109. ASSERT(file.substr(0, rootSize) == m_root);
  110. m_filesToParse.insert(file.substr(rootSize));
  111. }
  112. }
  113. void Package::getFiles(std::vector<std::string> &files) const {
  114. ASSERT(m_filesToParse.empty());
  115. files.clear();
  116. files.reserve(m_files.size());
  117. for (unsigned int i = 0; i < m_files.size(); i++) {
  118. const char *fileName = m_files.at(i);
  119. files.push_back(fileName);
  120. }
  121. }
  122. FileCachePtr Package::getFileCache() {
  123. for (set<string>::const_iterator iter = m_directories.begin();
  124. iter != m_directories.end(); ++iter) {
  125. vector<string> files;
  126. Util::find(files, m_root, iter->c_str(), false,
  127. &Option::PackageExcludeStaticDirs,
  128. &Option::PackageExcludeStaticFiles);
  129. Option::FilterFiles(files, Option::PackageExcludeStaticPatterns);
  130. for (unsigned int i = 0; i < files.size(); i++) {
  131. string &file = files[i];
  132. string rpath = file.substr(m_root.size());
  133. if (!m_fileCache->fileExists(rpath.c_str())) {
  134. Logger::Verbose("saving %s", file.c_str());
  135. m_fileCache->write(rpath.c_str(), file.c_str());
  136. }
  137. }
  138. }
  139. for (set<string>::const_iterator iter = m_staticDirectories.begin();
  140. iter != m_staticDirectories.end(); ++iter) {
  141. vector<string> files;
  142. Util::find(files, m_root, iter->c_str(), false);
  143. for (unsigned int i = 0; i < files.size(); i++) {
  144. string &file = files[i];
  145. string rpath = file.substr(m_root.size());
  146. if (!m_fileCache->fileExists(rpath.c_str())) {
  147. Logger::Verbose("saving %s", file.c_str());
  148. m_fileCache->write(rpath.c_str(), file.c_str());
  149. }
  150. }
  151. }
  152. for (set<string>::const_iterator iter = m_extraStaticFiles.begin();
  153. iter != m_extraStaticFiles.end(); ++iter) {
  154. const char *file = iter->c_str();
  155. if (!m_fileCache->fileExists(file)) {
  156. string fullpath = m_root + file;
  157. Logger::Verbose("saving %s", fullpath.c_str());
  158. m_fileCache->write(file, fullpath.c_str());
  159. }
  160. }
  161. for (map<string,string>::const_iterator
  162. iter = m_discoveredStaticFiles.begin();
  163. iter != m_discoveredStaticFiles.end(); ++iter) {
  164. const char *file = iter->first.c_str();
  165. if (!m_fileCache->fileExists(file)) {
  166. const char *fullpath = iter->second.c_str();
  167. Logger::Verbose("saving %s", fullpath[0] ? fullpath : file);
  168. if (fullpath[0]) {
  169. m_fileCache->write(file, fullpath);
  170. } else {
  171. m_fileCache->write(file);
  172. }
  173. }
  174. }
  175. return m_fileCache;
  176. }
  177. ///////////////////////////////////////////////////////////////////////////////
  178. class ParserWorker :
  179. public JobQueueWorker<std::pair<const char *,bool>, true, true> {
  180. public:
  181. bool m_ret;
  182. ParserWorker() : m_ret(true) {}
  183. virtual void doJob(JobType job) {
  184. bool ret;
  185. try {
  186. Package *package = (Package*)m_opaque;
  187. ret = package->parseImpl(job.first);
  188. } catch (Exception &e) {
  189. Logger::Error("%s", e.getMessage().c_str());
  190. ret = false;
  191. } catch (...) {
  192. Logger::Error("Fatal: An unexpected exception was thrown");
  193. m_ret = false;
  194. return;
  195. }
  196. if (!ret && job.second) {
  197. Logger::Error("Fatal: Unable to stat/parse %s", job.first);
  198. m_ret = false;
  199. }
  200. }
  201. };
  202. void Package::addSourceFile(const char *fileName, bool check /* = false */) {
  203. if (fileName && *fileName) {
  204. Lock lock(m_mutex);
  205. bool inserted = m_filesToParse.insert(Util::canonicalize(fileName)).second;
  206. if (inserted && m_dispatcher) {
  207. ((JobQueueDispatcher<ParserWorker::JobType,
  208. ParserWorker>*)m_dispatcher)->enqueue(
  209. make_pair(m_files.add(fileName), check));
  210. }
  211. }
  212. }
  213. bool Package::parse(bool check) {
  214. if (m_filesToParse.empty()) {
  215. return true;
  216. }
  217. unsigned int threadCount = Option::ParserThreadCount;
  218. if (threadCount > m_filesToParse.size()) {
  219. threadCount = m_filesToParse.size();
  220. }
  221. if (threadCount <= 0) threadCount = 1;
  222. JobQueueDispatcher<ParserWorker::JobType, ParserWorker>
  223. dispatcher(threadCount, true, 0, false, this);
  224. m_dispatcher = &dispatcher;
  225. std::set<string> files;
  226. files.swap(m_filesToParse);
  227. dispatcher.start();
  228. for (std::set<string>::iterator iter = files.begin(), end = files.end();
  229. iter != end; ++iter) {
  230. addSourceFile((*iter).c_str(), check);
  231. }
  232. dispatcher.waitEmpty();
  233. m_dispatcher = 0;
  234. std::vector<ParserWorker*> workers;
  235. dispatcher.getWorkers(workers);
  236. for (unsigned int i = 0; i < workers.size(); i++) {
  237. ParserWorker *worker = workers[i];
  238. if (!worker->m_ret) return false;
  239. }
  240. return true;
  241. }
  242. bool Package::parse(const char *fileName) {
  243. return parseImpl(m_files.add(fileName));
  244. }
  245. bool Package::parseImpl(const char *fileName) {
  246. ASSERT(fileName);
  247. if (fileName[0] == 0) return false;
  248. string fullPath;
  249. if (fileName[0] == '/') {
  250. fullPath = fileName;
  251. } else {
  252. fullPath = m_root + fileName;
  253. }
  254. struct stat sb;
  255. if (stat(fullPath.c_str(), &sb)) {
  256. if (fullPath.find(' ') == string::npos) {
  257. Logger::Error("Unable to stat file %s", fullPath.c_str());
  258. }
  259. return false;
  260. }
  261. if ((sb.st_mode & S_IFMT) == S_IFDIR) {
  262. Logger::Error("Unable to parse directory: %s", fullPath.c_str());
  263. return false;
  264. }
  265. int lines = 0;
  266. try {
  267. Logger::Verbose("parsing %s ...", fullPath.c_str());
  268. Scanner scanner(fullPath.c_str(), Option::ScannerType);
  269. Compiler::Parser parser(scanner, fileName, m_ar, sb.st_size);
  270. try {
  271. if (!parser.parse()) {
  272. throw Exception("Unable to parse file: %s %s", fullPath.c_str(),
  273. parser.getMessage().c_str());
  274. }
  275. } catch (...) {
  276. parser.failed();
  277. throw;
  278. }
  279. lines = parser.line1();
  280. } catch (FileOpenException &e) {
  281. Logger::Error("%s", e.getMessage().c_str());
  282. return false;
  283. }
  284. m_lineCount += lines;
  285. struct stat fst;
  286. stat(fullPath.c_str(), &fst);
  287. m_charCount += fst.st_size;
  288. Lock lock(m_mutex);
  289. if (m_extraStaticFiles.find(fileName) == m_extraStaticFiles.end() &&
  290. m_discoveredStaticFiles.find(fileName) == m_discoveredStaticFiles.end()) {
  291. if (Option::CachePHPFile) {
  292. m_discoveredStaticFiles[fileName] = fullPath;
  293. } else {
  294. m_discoveredStaticFiles[fileName] = "";
  295. }
  296. }
  297. return true;
  298. }
  299. ///////////////////////////////////////////////////////////////////////////////
  300. void Package::saveStatsToFile(const char *filename, int totalSeconds) const {
  301. ofstream f(filename);
  302. if (f) {
  303. JSON::CodeError::OutputStream o(f, m_ar);
  304. JSON::CodeError::MapStream ms(o);
  305. ms.add("FileCount", getFileCount())
  306. .add("LineCount", getLineCount())
  307. .add("CharCount", getCharCount())
  308. .add("FunctionCount", m_ar->getFunctionCount())
  309. .add("ClassCount", m_ar->getClassCount())
  310. .add("TotalTime", totalSeconds);
  311. if (getLineCount()) {
  312. ms.add("AvgCharPerLine", getCharCount() / getLineCount());
  313. }
  314. if (m_ar->getFunctionCount()) {
  315. ms.add("AvgLinePerFunc", getLineCount()/m_ar->getFunctionCount());
  316. }
  317. std::map<std::string, int> counts;
  318. SymbolTable::CountTypes(counts);
  319. m_ar->countReturnTypes(counts);
  320. ms.add("SymbolTypes");
  321. o << counts;
  322. ms.add("VariableTableFunctions");
  323. JSON::CodeError::ListStream ls(o);
  324. BOOST_FOREACH(const std::string &f, m_ar->m_variableTableFunctions) {
  325. ls << f;
  326. }
  327. ls.done();
  328. ms.done();
  329. f.close();
  330. }
  331. }
  332. int Package::saveStatsToDB(ServerDataPtr server, int totalSeconds,
  333. const std::string &branch, int revision) const {
  334. std::map<std::string, int> counts;
  335. SymbolTable::CountTypes(counts);
  336. m_ar->countReturnTypes(counts);
  337. ostringstream sout;
  338. JSON::CodeError::OutputStream o(sout, m_ar);
  339. o << counts;
  340. DBConn conn;
  341. conn.open(server);
  342. const char *sql = "INSERT INTO hphp_run (branch, revision, file, line, "
  343. "byte, program, function, class, types, time)";
  344. DBQuery q(&conn, sql);
  345. q.insert("'%s', %d, %d, %d, %d, %d, %d, %d, '%s', %d",
  346. branch.c_str(), revision,
  347. getFileCount(), getLineCount(), getCharCount(),
  348. 1, m_ar->getFunctionCount(),
  349. m_ar->getClassCount(), sout.str().c_str(), totalSeconds);
  350. q.execute();
  351. return conn.getLastInsertId();
  352. }
  353. void Package::commitStats(ServerDataPtr server, int runId) const {
  354. DBConn conn;
  355. conn.open(server);
  356. {
  357. DBQuery q(&conn, "UPDATE hphp_dep");
  358. q.setField("parent_file = parent");
  359. q.filterBy("run = %d", runId);
  360. q.filterBy("kind IN ('PHPInclude', 'PHPTemplate')");
  361. q.execute();
  362. }
  363. {
  364. DBQuery q(&conn, "UPDATE hphp_run");
  365. q.setField("committed = 1");
  366. q.filterBy("id = %d", runId);
  367. q.execute();
  368. }
  369. }