stackmock.cpp 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373
  1. //
  2. // Copyright (c) 2017-2021, Manticore Software LTD (https://manticoresearch.com)
  3. // Copyright (c) 2001-2016, Andrew Aksyonoff
  4. // Copyright (c) 2008-2016, Sphinx Technologies Inc
  5. // All rights reserved
  6. //
  7. // This program is free software; you can redistribute it and/or modify
  8. // it under the terms of the GNU General Public License. You should have
  9. // received a copy of the GPL license along with this program; if you
  10. // did not, you can find it at http://www.gnu.org/
  11. //
  12. #include "stackmock.h"
  13. #include "sphinxexpr.h"
  14. #include "coroutine.h"
  15. #include "searchdsql.h"
  16. #include "attribute.h"
  17. // hard-coded definitions to avoid probing (that is - to avoid confusing memcheck programs)
  18. // run searchd with --logdebug --console once, read values, then write them here and uncomment these lines
  19. //#define KNOWN_CREATE_SIZE 4208
  20. //#define KNOWN_EXPR_SIZE 48
  21. //#define KNOWN_FILTER_SIZE 400
  22. class StackMeasurer_c
  23. {
  24. protected:
  25. CSphFixedVector<BYTE> m_dMockStack { (int) Threads::GetDefaultCoroStackSize () };
  26. int m_iComplexity;
  27. protected:
  28. int CalcUsedStackEdge ( BYTE uFiller )
  29. {
  30. ARRAY_CONSTFOREACH ( i, m_dMockStack )
  31. if ( m_dMockStack[i]!=uFiller )
  32. return m_dMockStack.GetLength ()-i;
  33. return m_dMockStack.GetLength ();
  34. }
  35. void MockInitMem ( BYTE uFiller )
  36. {
  37. ::memset ( m_dMockStack.begin (), uFiller, m_dMockStack.GetLengthBytes () );
  38. }
  39. int MeasureStackWithPattern ( BYTE uPattern )
  40. {
  41. MockInitMem ( uPattern );
  42. MockParseTest ();
  43. auto iUsedStackEdge = CalcUsedStackEdge ( uPattern );
  44. return sphRoundUp ( iUsedStackEdge, 4 );
  45. }
  46. int MeasureStack ()
  47. {
  48. auto iStartStackDE = MeasureStackWithPattern ( 0xDE );
  49. auto iStartStackAD = MeasureStackWithPattern ( 0xAD );
  50. return Max ( iStartStackDE, iStartStackAD );
  51. }
  52. virtual void BuildMockExpr ( int iComplexity ) = 0;
  53. virtual void MockParseTest () = 0;
  54. void BuildMockExprWrapper ( int iComplexity )
  55. {
  56. m_iComplexity = iComplexity + 1;
  57. BuildMockExpr ( iComplexity );
  58. }
  59. public:
  60. int MockMeasureStack ( int iNodes )
  61. {
  62. BuildMockExprWrapper ( 0 );
  63. int iStartStack = MeasureStack ();
  64. int iDelta = 0;
  65. // Find edge of stack where expr length became visible
  66. // (we need quite big expr in order to touch deepest of the stack)
  67. int iHeight = 0;
  68. while ( iDelta<=0 )
  69. {
  70. ++iHeight;
  71. BuildMockExprWrapper ( iHeight );
  72. auto iCurStack = MeasureStack ();
  73. iDelta = iCurStack - iStartStack;
  74. }
  75. iStartStack += iDelta;
  76. // add iNodes frames and average stack from them
  77. BuildMockExprWrapper ( iHeight + iNodes );
  78. auto iCurStack = MeasureStack ();
  79. iDelta = iCurStack-iStartStack;
  80. iDelta/=iNodes;
  81. iDelta = sphRoundUp ( iDelta, 16 );
  82. return iDelta;
  83. }
  84. virtual ~StackMeasurer_c () = default;
  85. };
  86. /////////////////////////////////////////////////////////////////////
  87. /// calculate stack for expressions
  88. class CreateExprStackSize_c : public StackMeasurer_c
  89. {
  90. void BuildMockExpr ( int iComplexity ) final
  91. {
  92. m_sExpr.Clear();
  93. m_sExpr << "((attr_a=0)*1)";
  94. for ( int i = 1; i<iComplexity+1; ++i ) // ((attr_a=0)*1) + ((attr_b=1)*3) + ((attr_b=2)*5) + ...
  95. m_sExpr << "+((attr_b=" << i << ")*" << i * 2+1 << ")";
  96. }
  97. void MockParseTest () override
  98. {
  99. struct
  100. {
  101. ExprParseArgs_t m_tArgs;
  102. CSphString m_sError;
  103. CSphSchema m_tSchema;
  104. const char * m_sExpr = nullptr;
  105. bool m_bSuccess = false;
  106. ISphExpr * m_pExprBase = nullptr;
  107. } tParams;
  108. CSphColumnInfo tAttr;
  109. tAttr.m_eAttrType = SPH_ATTR_INTEGER;
  110. tAttr.m_sName = "attr_a";
  111. tParams.m_tSchema.AddAttr ( tAttr, false );
  112. tAttr.m_sName = "attr_b";
  113. tParams.m_tSchema.AddAttr ( tAttr, false );
  114. tParams.m_sExpr = m_sExpr.cstr();
  115. Threads::MockCallCoroutine ( m_dMockStack, [&tParams] {
  116. tParams.m_pExprBase = sphExprParse ( tParams.m_sExpr, tParams.m_tSchema, tParams.m_sError, tParams.m_tArgs );
  117. } );
  118. tParams.m_bSuccess = !!tParams.m_pExprBase;
  119. SafeRelease ( tParams.m_pExprBase );
  120. if ( !tParams.m_bSuccess || !tParams.m_sError.IsEmpty () )
  121. sphWarning ( "stack check expression error: %s", tParams.m_sError.cstr () );
  122. }
  123. protected:
  124. StringBuilder_c m_sExpr;
  125. public:
  126. static int MockMeasure();
  127. static void PublishValue (int iStack);
  128. };
  129. // measure stack for evaluate expression
  130. class EvalExprStackSize_c : public CreateExprStackSize_c
  131. {
  132. void MockParseTest () override
  133. {
  134. struct
  135. {
  136. ExprParseArgs_t m_tArgs;
  137. CSphString m_sError;
  138. CSphSchema m_tSchema;
  139. const char * m_sExpr = nullptr;
  140. bool m_bSuccess = false;
  141. ISphExpr * m_pExprBase = nullptr;
  142. CSphMatch m_tMatch;
  143. } tParams;
  144. CSphColumnInfo tAttr;
  145. tAttr.m_eAttrType = SPH_ATTR_INTEGER;
  146. tAttr.m_sName = "attr_a";
  147. tParams.m_tSchema.AddAttr ( tAttr, false );
  148. tAttr.m_sName = "attr_b";
  149. tParams.m_tSchema.AddAttr ( tAttr, false );
  150. CSphFixedVector<CSphRowitem> dRow { tParams.m_tSchema.GetRowSize () };
  151. auto * pRow = dRow.Begin();
  152. for ( int i = 1; i<tParams.m_tSchema.GetAttrsCount (); ++i )
  153. sphSetRowAttr ( pRow, tParams.m_tSchema.GetAttr ( i ).m_tLocator, i );
  154. sphSetRowAttr ( pRow, tParams.m_tSchema.GetAttr ( 0 ).m_tLocator, 123 );
  155. tParams.m_tMatch.m_tRowID = 123;
  156. tParams.m_tMatch.m_iWeight = 456;
  157. tParams.m_tMatch.m_pStatic = pRow;
  158. tParams.m_sExpr = m_sExpr.cstr();
  159. { // parse in dedicated coro (hope, 100K frame per level should fit any arch)
  160. CSphFixedVector<BYTE> dSafeStack { m_iComplexity * 100 * 1024 };
  161. Threads::MockCallCoroutine ( dSafeStack, [&tParams] { // do in coro as for fat expr it might already require dedicated stack
  162. tParams.m_pExprBase = sphExprParse ( tParams.m_sExpr, tParams.m_tSchema, tParams.m_sError, tParams.m_tArgs );
  163. });
  164. }
  165. tParams.m_bSuccess = !!tParams.m_pExprBase;
  166. assert ( tParams.m_pExprBase );
  167. Threads::MockCallCoroutine ( m_dMockStack, [&tParams] {
  168. tParams.m_pExprBase->Eval ( tParams.m_tMatch );
  169. } );
  170. if ( !tParams.m_bSuccess || !tParams.m_sError.IsEmpty () )
  171. sphWarning ( "stack check expression error: %s", tParams.m_sError.cstr () );
  172. }
  173. public:
  174. static int MockMeasure();
  175. static void PublishValue ( int iStack );
  176. };
  177. /////////////////////////////////////////////////////////////////////
  178. class FilterCreationMeasureStack_c : public StackMeasurer_c
  179. {
  180. void BuildMockExpr ( int iComplexity ) final
  181. {
  182. m_sQuery.Clear ();
  183. m_sQuery << "select * from test where id between 1 and 10";
  184. for ( int i = 0; i<iComplexity; i++ )
  185. m_sQuery << " OR id between 1 and 10";
  186. }
  187. void MockParseTest () final
  188. {
  189. struct
  190. {
  191. CSphString m_sQuery;
  192. CSphVector<SqlStmt_t> m_dStmt;
  193. CSphSchema m_tSchema;
  194. CSphString m_sError;
  195. bool m_bSuccess = false;
  196. } tParams;
  197. tParams.m_sQuery = m_sQuery.cstr();
  198. CSphColumnInfo tAttr;
  199. tAttr.m_eAttrType = SPH_ATTR_BIGINT;
  200. tAttr.m_sName = sphGetDocidName ();
  201. tParams.m_tSchema.AddAttr ( tAttr, false );
  202. Threads::MockCallCoroutine ( m_dMockStack, [&tParams] {
  203. tParams.m_bSuccess = sphParseSqlQuery ( tParams.m_sQuery.cstr (), tParams.m_sQuery.Length ()
  204. , tParams.m_dStmt, tParams.m_sError, SPH_COLLATION_DEFAULT );
  205. if ( !tParams.m_bSuccess )
  206. return;
  207. const CSphQuery & tQuery = tParams.m_dStmt[0].m_tQuery;
  208. CreateFilterContext_t tFCtx;
  209. tFCtx.m_pFilters = &tQuery.m_dFilters;
  210. tFCtx.m_pFilterTree = &tQuery.m_dFilterTree;
  211. tFCtx.m_pSchema = &tParams.m_tSchema;
  212. tFCtx.m_bScan = true;
  213. CSphString sWarning;
  214. CSphQueryContext tCtx ( tQuery );
  215. tParams.m_bSuccess = tCtx.CreateFilters ( tFCtx, tParams.m_sError, sWarning );
  216. } );
  217. if ( !tParams.m_bSuccess || !tParams.m_sError.IsEmpty () )
  218. sphWarning ( "stack check filter error: %s", tParams.m_sError.cstr () );
  219. }
  220. protected:
  221. StringBuilder_c m_sQuery;
  222. public:
  223. static int MockMeasure();
  224. static void PublishValue ( int iStack );
  225. };
  226. int CreateExprStackSize_c::MockMeasure()
  227. {
  228. CreateExprStackSize_c tCreateMeter;
  229. return tCreateMeter.MockMeasureStack ( 5 );
  230. }
  231. int EvalExprStackSize_c::MockMeasure()
  232. {
  233. EvalExprStackSize_c tEvalMeter;
  234. return tEvalMeter.MockMeasureStack ( 20 );
  235. }
  236. int FilterCreationMeasureStack_c::MockMeasure()
  237. {
  238. FilterCreationMeasureStack_c tCreateMeter;
  239. return tCreateMeter.MockMeasureStack ( 100 );
  240. }
  241. void CreateExprStackSize_c::PublishValue ( int iStack )
  242. {
  243. SetExprNodeStackItemSize ( iStack, 0 );
  244. }
  245. void EvalExprStackSize_c::PublishValue ( int iStack )
  246. {
  247. SetExprNodeStackItemSize ( 0, iStack );
  248. }
  249. void FilterCreationMeasureStack_c::PublishValue ( int iStack )
  250. {
  251. SetFilterStackItemSize ( iStack );
  252. }
  253. template<typename MOCK, int COMPILEDVAL>
  254. void DetermineStackSize ( const char* szReport, const char* szEnv )
  255. {
  256. int iSize = COMPILEDVAL;
  257. int iNewSize = 0;
  258. bool bMocked = false;
  259. if ( !( COMPILEDVAL && Threads::IsUnderValgrind() ) )
  260. {
  261. StringBuilder_c sName;
  262. sName << "MANTICORE_" << szEnv;
  263. iNewSize = val_from_env ( sName.cstr(), 0 );
  264. if ( !iNewSize )
  265. {
  266. iNewSize = MOCK::MockMeasure();
  267. bMocked = true;
  268. #ifdef NDEBUG
  269. if ( COMPILEDVAL && COMPILEDVAL < iNewSize )
  270. sphWarning ( "Compiled-in value %s (%d) is less than measured (%d). Consider to fix the value!", szEnv, COMPILEDVAL, iNewSize );
  271. #endif
  272. }
  273. iSize = iNewSize;
  274. if ( bMocked )
  275. sphLogDebug ( "%s is %d. Consider to add env MANTICORE_%s=%d to store this value persistent for this binary", szReport, iSize, szEnv, iNewSize );
  276. else
  277. sphLogDebug ( "%s %d (from env MANTICORE_%s)", szReport, iSize, szEnv );
  278. } else
  279. {
  280. sphLogDebug ( "%s is %d (compiled-in)", szReport, iSize );
  281. }
  282. MOCK::PublishValue ( iSize );
  283. }
  284. void DetermineNodeItemStackSize()
  285. {
  286. // some values for x86_64: clang 12.0.1 relwithdebinfo = 768, debug = 4208. gcc 9.3 relwithdebinfo = 16, debug = 256
  287. #ifdef KNOWN_CREATE_SIZE
  288. DetermineStackSize<CreateExprStackSize_c, KNOWN_CREATE_SIZE>
  289. #else
  290. DetermineStackSize<CreateExprStackSize_c, 0>
  291. #endif
  292. ( "expression stack for creation", "KNOWN_CREATE_SIZE" );
  293. // some values for x86_64: clang 12.0.1 relwithdebinfo = 32, debug = 48. gcc 9.3 relwithdebinfo = 48, debug = 48
  294. #ifdef KNOWN_EXPR_SIZE
  295. DetermineStackSize<EvalExprStackSize_c, KNOWN_EXPR_SIZE>
  296. #else
  297. DetermineStackSize<EvalExprStackSize_c, 0>
  298. #endif
  299. ( "expression stack for eval/deletion", "KNOWN_EXPR_SIZE" );
  300. }
  301. void DetermineFilterItemStackSize ()
  302. {
  303. // some values for x86_64: clang 12.0.1 relwithdebinfo = 208, debug = 400. gcc 9.3 relwithdebinfo = 240, debug = 272
  304. #ifdef KNOWN_FILTER_SIZE
  305. DetermineStackSize<FilterCreationMeasureStack_c, KNOWN_FILTER_SIZE>
  306. #else
  307. DetermineStackSize<FilterCreationMeasureStack_c, 0>
  308. #endif
  309. ( "filter stack delta", "KNOWN_FILTER_SIZE" );
  310. }