PageRenderTime 48ms CodeModel.GetById 15ms RepoModel.GetById 0ms app.codeStats 1ms

/src/compiler/package.cpp

https://github.com/kevlund/hiphop-php
C++ | 414 lines | 347 code | 49 blank | 18 comment | 71 complexity | 4b8175c7689b3d4753d618a85bdd42b0 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. }
  192. if (!ret && job.second) {
  193. Logger::Error("Fatal: Unable to stat/parse %s", job.first);
  194. m_ret = false;
  195. }
  196. }
  197. };
  198. void Package::addSourceFile(const char *fileName, bool check /* = false */) {
  199. if (fileName && *fileName) {
  200. Lock lock(m_mutex);
  201. bool inserted = m_filesToParse.insert(Util::canonicalize(fileName)).second;
  202. if (inserted && m_dispatcher) {
  203. ((JobQueueDispatcher<ParserWorker::JobType,
  204. ParserWorker>*)m_dispatcher)->enqueue(
  205. make_pair(m_files.add(fileName), check));
  206. }
  207. }
  208. }
  209. bool Package::parse(bool check) {
  210. if (m_filesToParse.empty()) {
  211. return true;
  212. }
  213. unsigned int threadCount = Option::ParserThreadCount;
  214. if (threadCount > m_filesToParse.size()) {
  215. threadCount = m_filesToParse.size();
  216. }
  217. if (threadCount <= 0) threadCount = 1;
  218. JobQueueDispatcher<ParserWorker::JobType, ParserWorker>
  219. dispatcher(threadCount, true, 0, this);
  220. m_dispatcher = &dispatcher;
  221. std::set<string> files;
  222. files.swap(m_filesToParse);
  223. dispatcher.start();
  224. for (std::set<string>::iterator iter = files.begin(), end = files.end();
  225. iter != end; ++iter) {
  226. addSourceFile((*iter).c_str(), check);
  227. }
  228. dispatcher.waitEmpty();
  229. m_dispatcher = 0;
  230. std::vector<ParserWorker*> workers;
  231. dispatcher.getWorkers(workers);
  232. for (unsigned int i = 0; i < workers.size(); i++) {
  233. ParserWorker *worker = workers[i];
  234. if (!worker->m_ret) return false;
  235. }
  236. return true;
  237. }
  238. bool Package::parse(const char *fileName) {
  239. return parseImpl(m_files.add(fileName));
  240. }
  241. bool Package::parseImpl(const char *fileName) {
  242. ASSERT(fileName);
  243. if (fileName[0] == 0) return false;
  244. string fullPath;
  245. if (fileName[0] == '/') {
  246. fullPath = fileName;
  247. } else {
  248. fullPath = m_root + fileName;
  249. }
  250. struct stat sb;
  251. if (stat(fullPath.c_str(), &sb)) {
  252. if (fullPath.find(' ') == string::npos) {
  253. Logger::Error("Unable to stat file %s", fullPath.c_str());
  254. }
  255. return false;
  256. }
  257. if ((sb.st_mode & S_IFMT) == S_IFDIR) {
  258. Logger::Error("Unable to parse directory: %s", fullPath.c_str());
  259. return false;
  260. }
  261. int lines = 0;
  262. try {
  263. Logger::Verbose("parsing %s ...", fullPath.c_str());
  264. Scanner scanner(fullPath.c_str(), Option::ScannerType);
  265. Compiler::Parser parser(scanner, fileName, m_ar, sb.st_size);
  266. try {
  267. if (!parser.parse()) {
  268. throw Exception("Unable to parse file: %s %s", fullPath.c_str(),
  269. parser.getMessage().c_str());
  270. }
  271. } catch (...) {
  272. parser.failed();
  273. throw;
  274. }
  275. lines = parser.line1();
  276. } catch (FileOpenException &e) {
  277. Logger::Error("%s", e.getMessage().c_str());
  278. return false;
  279. }
  280. m_lineCount += lines;
  281. struct stat fst;
  282. stat(fullPath.c_str(), &fst);
  283. m_charCount += fst.st_size;
  284. Lock lock(m_mutex);
  285. if (m_extraStaticFiles.find(fileName) == m_extraStaticFiles.end() &&
  286. m_discoveredStaticFiles.find(fileName) == m_discoveredStaticFiles.end()) {
  287. if (Option::CachePHPFile) {
  288. m_discoveredStaticFiles[fileName] = fullPath;
  289. } else {
  290. m_discoveredStaticFiles[fileName] = "";
  291. }
  292. }
  293. return true;
  294. }
  295. ///////////////////////////////////////////////////////////////////////////////
  296. void Package::saveStatsToFile(const char *filename, int totalSeconds) const {
  297. ofstream f(filename);
  298. if (f) {
  299. JSON::CodeError::OutputStream o(f, m_ar);
  300. JSON::CodeError::MapStream ms(o);
  301. ms.add("FileCount", getFileCount())
  302. .add("LineCount", getLineCount())
  303. .add("CharCount", getCharCount())
  304. .add("FunctionCount", m_ar->getFunctionCount())
  305. .add("ClassCount", m_ar->getClassCount())
  306. .add("TotalTime", totalSeconds);
  307. if (getLineCount()) {
  308. ms.add("AvgCharPerLine", getCharCount() / getLineCount());
  309. }
  310. if (m_ar->getFunctionCount()) {
  311. ms.add("AvgLinePerFunc", getLineCount()/m_ar->getFunctionCount());
  312. }
  313. std::map<std::string, int> counts;
  314. SymbolTable::CountTypes(counts);
  315. m_ar->countReturnTypes(counts);
  316. ms.add("SymbolTypes");
  317. o << counts;
  318. ms.add("VariableTableFunctions");
  319. JSON::CodeError::ListStream ls(o);
  320. BOOST_FOREACH(const std::string &f, m_ar->m_variableTableFunctions) {
  321. ls << f;
  322. }
  323. ls.done();
  324. ms.done();
  325. f.close();
  326. }
  327. }
  328. int Package::saveStatsToDB(ServerDataPtr server, int totalSeconds,
  329. const std::string &branch, int revision) const {
  330. std::map<std::string, int> counts;
  331. SymbolTable::CountTypes(counts);
  332. m_ar->countReturnTypes(counts);
  333. ostringstream sout;
  334. JSON::CodeError::OutputStream o(sout, m_ar);
  335. o << counts;
  336. DBConn conn;
  337. conn.open(server);
  338. const char *sql = "INSERT INTO hphp_run (branch, revision, file, line, "
  339. "byte, program, function, class, types, time)";
  340. DBQuery q(&conn, sql);
  341. q.insert("'%s', %d, %d, %d, %d, %d, %d, %d, '%s', %d",
  342. branch.c_str(), revision,
  343. getFileCount(), getLineCount(), getCharCount(),
  344. 1, m_ar->getFunctionCount(),
  345. m_ar->getClassCount(), sout.str().c_str(), totalSeconds);
  346. q.execute();
  347. return conn.getLastInsertId();
  348. }
  349. void Package::commitStats(ServerDataPtr server, int runId) const {
  350. DBConn conn;
  351. conn.open(server);
  352. {
  353. DBQuery q(&conn, "UPDATE hphp_dep");
  354. q.setField("parent_file = parent");
  355. q.filterBy("run = %d", runId);
  356. q.filterBy("kind IN ('PHPInclude', 'PHPTemplate')");
  357. q.execute();
  358. }
  359. {
  360. DBQuery q(&conn, "UPDATE hphp_run");
  361. q.setField("committed = 1");
  362. q.filterBy("id = %d", runId);
  363. q.execute();
  364. }
  365. }